Loading core/java/android/print/IPrintSpooler.aidl +0 −1 Original line number Diff line number Diff line Loading @@ -36,7 +36,6 @@ import android.print.PrintJobInfo; */ oneway interface IPrintSpooler { void removeObsoletePrintJobs(); void forgetPrintJobs(in List<PrintJobId> printJob); void getPrintJobInfos(IPrintSpoolerCallbacks callback, in ComponentName componentName, int state, int appId, int sequence); void getPrintJobInfo(in PrintJobId printJobId, IPrintSpoolerCallbacks callback, Loading core/java/android/print/IPrintSpoolerClient.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -29,5 +29,5 @@ oneway interface IPrintSpoolerClient { void onPrintJobQueued(in PrintJobInfo printJob); void onAllPrintJobsForServiceHandled(in ComponentName printService); void onAllPrintJobsHandled(); void onPrintJobStateChanged(in PrintJobId printJobId, int appId); void onPrintJobStateChanged(in PrintJobInfo printJob); } packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java +83 −44 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.print.PrintManager; import android.print.PrinterId; import android.print.PrinterInfo; import android.text.TextUtils; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.Log; import android.util.Slog; Loading @@ -59,10 +60,12 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; Loading @@ -82,6 +85,8 @@ public final class PrintSpoolerService extends Service { private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000; private static final String PRINT_JOB_FILE_PREFIX = "print_job_"; private static final String PRINT_FILE_EXTENSION = "pdf"; private static final Object sLock = new Object(); Loading Loading @@ -168,9 +173,9 @@ public final class PrintSpoolerService extends Service { PrintSpoolerService.this, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender(); Message message = mHandlerCaller.obtainMessageIIO( Message message = mHandlerCaller.obtainMessageO( HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED, printJob.getAppId(), 0, printJob.getId()); printJob); mHandlerCaller.executeOrSendMessage(message); message = mHandlerCaller.obtainMessageOO( Loading @@ -179,9 +184,6 @@ public final class PrintSpoolerService extends Service { mHandlerCaller.executeOrSendMessage(message); printJob.setCreationTime(System.currentTimeMillis()); synchronized (mLock) { mPersistanceManager.writeStateLocked(); } } @Override Loading Loading @@ -225,12 +227,40 @@ public final class PrintSpoolerService extends Service { } @Override public void forgetPrintJobs(List<PrintJobId> printJobIds) { PrintSpoolerService.this.forgetPrintJobs(printJobIds); protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { PrintSpoolerService.this.dump(fd, writer, args); } }; } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mLock) { String prefix = args[0]; String tab = " "; pw.append(prefix).append("print jobs:").println(); final int printJobCount = mPrintJobs.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = mPrintJobs.get(i); pw.append(prefix).append(tab).append(printJob.toString()); pw.println(); } pw.append(prefix).append("print job files:").println(); File[] files = getFilesDir().listFiles(); if (files != null) { final int fileCount = files.length; for (int i = 0; i < fileCount; i++) { File file = files[i]; if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) { pw.append(prefix).append(tab).append(file.getName()).println(); } } } } } private void sendOnPrintJobQueued(PrintJobInfo printJob) { Message message = mHandlerCaller.obtainMessageO( HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob); Loading Loading @@ -324,10 +354,9 @@ public final class PrintSpoolerService extends Service { case MSG_ON_PRINT_JOB_STATE_CHANGED: { if (mClient != null) { PrintJobId printJobId = (PrintJobId) message.obj; final int appId = message.arg1; PrintJobInfo printJob = (PrintJobInfo) message.obj; try { mClient.onPrintJobStateChanged(printJobId, appId); mClient.onPrintJobStateChanged(printJob); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error notify for print job state change.", re); } Loading Loading @@ -391,17 +420,46 @@ public final class PrintSpoolerService extends Service { public void createPrintJob(PrintJobInfo printJob) { synchronized (mLock) { addPrintJobLocked(printJob); setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null); } } private void handleReadPrintJobsLocked() { // Make a map with the files for a print job since we may have // to delete some. One example of getting orphan files if the // spooler crashes while constructing a print job. We do not // persist partially populated print jobs under construction to // avoid special handling for various attributes missing. ArrayMap<PrintJobId, File> fileForJobMap = null; File[] files = getFilesDir().listFiles(); if (files != null) { final int fileCount = files.length; for (int i = 0; i < fileCount; i++) { File file = files[i]; if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) { if (fileForJobMap == null) { fileForJobMap = new ArrayMap<PrintJobId, File>(); } String printJobIdString = file.getName().substring(0, PRINT_JOB_FILE_PREFIX.length()); PrintJobId printJobId = PrintJobId.unflattenFromString( printJobIdString); fileForJobMap.put(printJobId, file); } } } final int printJobCount = mPrintJobs.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = mPrintJobs.get(i); // We want to have only the orphan files at the end. if (fileForJobMap != null) { fileForJobMap.remove(printJob.getId()); } // Update the notification. mNotificationController.onPrintJobStateChanged(printJob); switch (printJob.getState()) { case PrintJobInfo.STATE_QUEUED: case PrintJobInfo.STATE_STARTED: Loading @@ -415,6 +473,15 @@ public final class PrintSpoolerService extends Service { } break; } } // Delete the orphan files. if (fileForJobMap != null) { final int orphanFileCount = fileForJobMap.size(); for (int i = 0; i < orphanFileCount; i++) { File file = fileForJobMap.valueAt(i); file.delete(); } } } public void checkAllPrintJobsHandled() { Loading Loading @@ -465,7 +532,7 @@ public final class PrintSpoolerService extends Service { } public File generateFileForPrintJob(PrintJobId printJobId) { return new File(getFilesDir(), "print_job_" return new File(getFilesDir(), PRINT_JOB_FILE_PREFIX + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION); } Loading @@ -476,31 +543,6 @@ public final class PrintSpoolerService extends Service { } } private void forgetPrintJobs(List<PrintJobId> printJobIds) { synchronized (mLock) { boolean printJobsRemoved = false; final int removedPrintJobCount = printJobIds.size(); for (int i = 0; i < removedPrintJobCount; i++) { PrintJobId removedPrintJobId = printJobIds.get(i); final int printJobCount = mPrintJobs.size(); for (int j = printJobCount - 1; j >= 0; j--) { PrintJobInfo printJob = mPrintJobs.get(j); if (removedPrintJobId.equals(printJob.getId())) { mPrintJobs.remove(j); printJobsRemoved = true; if (DEBUG_PRINT_JOB_LIFECYCLE) { Slog.i(LOG_TAG, "[FORGOT] " + printJob.getId().flattenToString()); } removePrintJobFileLocked(printJob.getId()); } } } if (printJobsRemoved) { mPersistanceManager.writeStateLocked(); } } } private void removeObsoletePrintJobs() { synchronized (mLock) { final int printJobCount = mPrintJobs.size(); Loading @@ -523,7 +565,7 @@ public final class PrintSpoolerService extends Service { if (file.exists()) { file.delete(); if (DEBUG_PRINT_JOB_LIFECYCLE) { Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId.flattenToString()); Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId); } } } Loading Loading @@ -552,10 +594,7 @@ public final class PrintSpoolerService extends Service { switch (state) { case PrintJobInfo.STATE_COMPLETED: case PrintJobInfo.STATE_CANCELED: // Just remove the file but keep the print job info since // the app that created it may be holding onto the PrintJob // instance and query it for its most recent state. We will // remove the info for this job when told so by the system. mPrintJobs.remove(printJob); removePrintJobFileLocked(printJob.getId()); // $fall-through$ Loading @@ -582,9 +621,9 @@ public final class PrintSpoolerService extends Service { notifyOnAllPrintJobsHandled(); } Message message = mHandlerCaller.obtainMessageIIO( Message message = mHandlerCaller.obtainMessageO( HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED, printJob.getAppId(), 0, printJob.getId()); printJob); mHandlerCaller.executeOrSendMessage(message); } } Loading services/java/com/android/server/print/RemotePrintSpooler.java +14 −42 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.print.IPrintSpoolerCallbacks; import android.print.IPrintSpoolerClient; import android.print.PrintJobId; import android.print.PrintJobInfo; import android.print.PrintManager; import android.util.Slog; import android.util.TimedRemoteCaller; Loading Loading @@ -91,7 +90,7 @@ final class RemotePrintSpooler { public static interface PrintSpoolerCallbacks { public void onPrintJobQueued(PrintJobInfo printJob); public void onAllPrintJobsForServiceHandled(ComponentName printService); public void onPrintJobStateChanged(PrintJobId printJobId, int appId); public void onPrintJobStateChanged(PrintJobInfo printJob); } public RemotePrintSpooler(Context context, int userId, Loading Loading @@ -280,30 +279,6 @@ final class RemotePrintSpooler { } } public final void forgetPrintJobs(List<PrintJobId> printJobIds) { throwIfCalledOnMainThread(); synchronized (mLock) { throwIfDestroyedLocked(); mCanUnbind = false; } try { getRemoteInstanceLazy().forgetPrintJobs(printJobIds); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error forgeting print jobs", re); } catch (TimeoutException te) { Slog.e(LOG_TAG, "Error forgeting print jobs", te); } finally { if (DEBUG) { Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] forgetPrintJobs()"); } synchronized (mLock) { mCanUnbind = true; mLock.notifyAll(); } } } public final void destroy() { throwIfCalledOnMainThread(); if (DEBUG) { Loading @@ -323,18 +298,15 @@ final class RemotePrintSpooler { .append(String.valueOf(mDestroyed)).println(); pw.append(prefix).append("bound=") .append((mRemoteInstance != null) ? "true" : "false").println(); pw.append(prefix).append("print jobs:").println(); if (mRemoteInstance != null) { List<PrintJobInfo> printJobs = getPrintJobInfos(null, PrintJobInfo.STATE_ANY, PrintManager.APP_ID_ANY); if (printJobs != null) { final int printJobCount = printJobs.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = printJobs.get(i); pw.append(prefix).append(prefix).append(printJob.toString()); pw.println(); } } pw.flush(); try { getRemoteInstanceLazy().asBinder().dump(fd, new String[]{prefix}); } catch (TimeoutException te) { /* ignore */ } catch (RemoteException re) { /* ignore */ } } } Loading @@ -346,8 +318,8 @@ final class RemotePrintSpooler { } } private void onPrintJobStateChanged(PrintJobId printJobId, int appId) { mCallbacks.onPrintJobStateChanged(printJobId, appId); private void onPrintJobStateChanged(PrintJobInfo printJob) { mCallbacks.onPrintJobStateChanged(printJob); } private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException { Loading Loading @@ -625,12 +597,12 @@ final class RemotePrintSpooler { } @Override public void onPrintJobStateChanged(PrintJobId printJobId, int appId) { public void onPrintJobStateChanged(PrintJobInfo printJob) { RemotePrintSpooler spooler = mWeakSpooler.get(); if (spooler != null) { final long identity = Binder.clearCallingIdentity(); try { spooler.onPrintJobStateChanged(printJobId, appId); spooler.onPrintJobStateChanged(printJob); } finally { Binder.restoreCallingIdentity(identity); } Loading services/java/com/android/server/print/UserState.java +139 −41 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserManager; import android.print.IPrintClient; import android.print.IPrintDocumentAdapter; import android.print.IPrintJobStateChangeListener; Loading @@ -52,6 +51,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import com.android.internal.R; import com.android.internal.os.BackgroundThread; Loading @@ -62,6 +62,7 @@ import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; Loading Loading @@ -93,8 +94,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { private final Set<ComponentName> mEnabledServices = new ArraySet<ComponentName>(); private final CreatedPrintJobTracker mCreatedPrintJobTracker = new CreatedPrintJobTracker(); private final PrintJobForAppCache mPrintJobForAppCache = new PrintJobForAppCache(); private final Object mLock; Loading Loading @@ -155,23 +156,22 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { public PrintJobInfo print(String printJobName, final IPrintClient client, final IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId) { PrintJobId printJobId = new PrintJobId(); // Track this job so we can forget it when the creator dies. if (!mCreatedPrintJobTracker.onPrintJobCreatedLocked(client.asBinder(), printJobId)) { // Not adding a print job means the client is dead - done. return null; } // Create print job place holder. final PrintJobInfo printJob = new PrintJobInfo(); printJob.setId(printJobId); printJob.setId(new PrintJobId()); printJob.setAppId(appId); printJob.setLabel(printJobName); printJob.setAttributes(attributes); printJob.setState(PrintJobInfo.STATE_CREATED); printJob.setCopies(1); // Track this job so we can forget it when the creator dies. if (!mPrintJobForAppCache.onPrintJobCreated(client.asBinder(), appId, printJob)) { // Not adding a print job means the client is dead - done. return null; } // Spin the spooler to add the job and show the config UI. new AsyncTask<Void, Void, Void>() { @Override Loading @@ -185,10 +185,40 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } public List<PrintJobInfo> getPrintJobInfos(int appId) { return mSpooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY, appId); List<PrintJobInfo> cachedPrintJobs = mPrintJobForAppCache.getPrintJobs(appId); // Note that the print spooler is not storing print jobs that // are in a terminal state as it is non-trivial to properly update // the spooler state for when to forget print jobs in terminal state. // Therefore, we fuse the cached print jobs for running apps (some // jobs are in a terminal state) with the ones that the print // spooler knows about (some jobs are being processed). ArrayMap<PrintJobId, PrintJobInfo> result = new ArrayMap<PrintJobId, PrintJobInfo>(); // Add the cached print jobs for running apps. final int cachedPrintJobCount = cachedPrintJobs.size(); for (int i = 0; i < cachedPrintJobCount; i++) { PrintJobInfo cachedPrintJob = cachedPrintJobs.get(i); result.put(cachedPrintJob.getId(), cachedPrintJob); } // Add everything else the spooler knows about. List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY, appId); final int printJobCount = printJobs.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = printJobs.get(i); result.put(printJob.getId(), printJob); } return new ArrayList<PrintJobInfo>(result.values()); } public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) { PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId); if (printJob != null) { return printJob; } return mSpooler.getPrintJobInfo(printJobId, appId); } Loading Loading @@ -398,9 +428,10 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } @Override public void onPrintJobStateChanged(PrintJobId printJobId, int appId) { public void onPrintJobStateChanged(PrintJobInfo printJob) { mPrintJobForAppCache.onPrintJobStateChanged(printJob); mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED, appId, 0, printJobId).sendToTarget(); printJob.getAppId(), 0, printJob.getId()).sendToTarget(); } @Override Loading Loading @@ -525,6 +556,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { pw.println(); } pw.append(prefix).append(tab).append("cached print jobs:").println(); mPrintJobForAppCache.dump(pw, prefix + tab + tab); pw.append(prefix).append(tab).append("discovery mediator:").println(); if (mPrinterDiscoverySession != null) { mPrinterDiscoverySession.dump(pw, prefix + tab + tab); Loading Loading @@ -1424,34 +1458,19 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } private final class CreatedPrintJobTracker { private final ArrayMap<IBinder, List<PrintJobId>> mCreatedPrintJobs = new ArrayMap<IBinder, List<PrintJobId>>(); private final class PrintJobForAppCache { private final SparseArray<List<PrintJobInfo>> mPrintJobsForRunningApp = new SparseArray<List<PrintJobInfo>>(); public boolean onPrintJobCreatedLocked(final IBinder creator, PrintJobId printJobId) { public boolean onPrintJobCreated(final IBinder creator, final int appId, PrintJobInfo printJob) { try { creator.linkToDeath(new DeathRecipient() { @Override public void binderDied() { creator.unlinkToDeath(this, 0); UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); // If the death is a result of the user being removed, then // do nothing since the spooler data for this user will be // wiped and we cannot bind to the spooler at this point. if (userManager.getUserInfo(mUserId) == null) { return; } List<PrintJobId> printJobIds = null; synchronized (mLock) { printJobIds = mCreatedPrintJobs.remove(creator); if (printJobIds == null) { return; } printJobIds = new ArrayList<PrintJobId>(printJobIds); } if (printJobIds != null) { mSpooler.forgetPrintJobs(printJobIds); mPrintJobsForRunningApp.remove(appId); } } }, 0); Loading @@ -1460,14 +1479,93 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { return false; } synchronized (mLock) { List<PrintJobId> printJobIds = mCreatedPrintJobs.get(creator); if (printJobIds == null) { printJobIds = new ArrayList<PrintJobId>(); mCreatedPrintJobs.put(creator, printJobIds); List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); if (printJobsForApp == null) { printJobsForApp = new ArrayList<PrintJobInfo>(); mPrintJobsForRunningApp.put(appId, printJobsForApp); } printJobIds.add(printJobId); printJobsForApp.add(printJob); } return true; } public void onPrintJobStateChanged(PrintJobInfo printJob) { synchronized (mLock) { List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get( printJob.getAppId()); if (printJobsForApp == null) { return; } final int printJobCount = printJobsForApp.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo oldPrintJob = printJobsForApp.get(i); if (oldPrintJob.getId().equals(printJob.getId())) { printJobsForApp.set(i, printJob); } } } } public PrintJobInfo getPrintJob(PrintJobId printJobId, int appId) { synchronized (mLock) { List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); if (printJobsForApp == null) { return null; } final int printJobCount = printJobsForApp.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = printJobsForApp.get(i); if (printJob.getId().equals(printJobId)) { return printJob; } } } return null; } public List<PrintJobInfo> getPrintJobs(int appId) { synchronized (mLock) { List<PrintJobInfo> printJobs = null; if (appId == PrintManager.APP_ID_ANY) { final int bucketCount = mPrintJobsForRunningApp.size(); for (int i = 0; i < bucketCount; i++) { List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); if (printJobs == null) { printJobs = new ArrayList<PrintJobInfo>(); } printJobs.addAll(bucket); } } else { List<PrintJobInfo> bucket = mPrintJobsForRunningApp.get(appId); if (bucket != null) { if (printJobs == null) { printJobs = new ArrayList<PrintJobInfo>(); } printJobs.addAll(bucket); } } if (printJobs != null) { return printJobs; } return Collections.emptyList(); } } public void dump(PrintWriter pw, String prefix) { synchronized (mLock) { String tab = " "; final int bucketCount = mPrintJobsForRunningApp.size(); for (int i = 0; i < bucketCount; i++) { final int appId = mPrintJobsForRunningApp.keyAt(i); pw.append(prefix).append("appId=" + appId).append(':').println(); List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); final int printJobCount = bucket.size(); for (int j = 0; j < printJobCount; j++) { PrintJobInfo printJob = bucket.get(j); pw.append(prefix).append(tab).append(printJob.toString()).println(); } } } } } } Loading
core/java/android/print/IPrintSpooler.aidl +0 −1 Original line number Diff line number Diff line Loading @@ -36,7 +36,6 @@ import android.print.PrintJobInfo; */ oneway interface IPrintSpooler { void removeObsoletePrintJobs(); void forgetPrintJobs(in List<PrintJobId> printJob); void getPrintJobInfos(IPrintSpoolerCallbacks callback, in ComponentName componentName, int state, int appId, int sequence); void getPrintJobInfo(in PrintJobId printJobId, IPrintSpoolerCallbacks callback, Loading
core/java/android/print/IPrintSpoolerClient.aidl +1 −1 Original line number Diff line number Diff line Loading @@ -29,5 +29,5 @@ oneway interface IPrintSpoolerClient { void onPrintJobQueued(in PrintJobInfo printJob); void onAllPrintJobsForServiceHandled(in ComponentName printService); void onAllPrintJobsHandled(); void onPrintJobStateChanged(in PrintJobId printJobId, int appId); void onPrintJobStateChanged(in PrintJobInfo printJob); }
packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java +83 −44 Original line number Diff line number Diff line Loading @@ -43,6 +43,7 @@ import android.print.PrintManager; import android.print.PrinterId; import android.print.PrinterInfo; import android.text.TextUtils; import android.util.ArrayMap; import android.util.AtomicFile; import android.util.Log; import android.util.Slog; Loading @@ -59,10 +60,12 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; Loading @@ -82,6 +85,8 @@ public final class PrintSpoolerService extends Service { private static final long CHECK_ALL_PRINTJOBS_HANDLED_DELAY = 5000; private static final String PRINT_JOB_FILE_PREFIX = "print_job_"; private static final String PRINT_FILE_EXTENSION = "pdf"; private static final Object sLock = new Object(); Loading Loading @@ -168,9 +173,9 @@ public final class PrintSpoolerService extends Service { PrintSpoolerService.this, 0, intent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_CANCEL_CURRENT).getIntentSender(); Message message = mHandlerCaller.obtainMessageIIO( Message message = mHandlerCaller.obtainMessageO( HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED, printJob.getAppId(), 0, printJob.getId()); printJob); mHandlerCaller.executeOrSendMessage(message); message = mHandlerCaller.obtainMessageOO( Loading @@ -179,9 +184,6 @@ public final class PrintSpoolerService extends Service { mHandlerCaller.executeOrSendMessage(message); printJob.setCreationTime(System.currentTimeMillis()); synchronized (mLock) { mPersistanceManager.writeStateLocked(); } } @Override Loading Loading @@ -225,12 +227,40 @@ public final class PrintSpoolerService extends Service { } @Override public void forgetPrintJobs(List<PrintJobId> printJobIds) { PrintSpoolerService.this.forgetPrintJobs(printJobIds); protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { PrintSpoolerService.this.dump(fd, writer, args); } }; } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mLock) { String prefix = args[0]; String tab = " "; pw.append(prefix).append("print jobs:").println(); final int printJobCount = mPrintJobs.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = mPrintJobs.get(i); pw.append(prefix).append(tab).append(printJob.toString()); pw.println(); } pw.append(prefix).append("print job files:").println(); File[] files = getFilesDir().listFiles(); if (files != null) { final int fileCount = files.length; for (int i = 0; i < fileCount; i++) { File file = files[i]; if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) { pw.append(prefix).append(tab).append(file.getName()).println(); } } } } } private void sendOnPrintJobQueued(PrintJobInfo printJob) { Message message = mHandlerCaller.obtainMessageO( HandlerCallerCallback.MSG_ON_PRINT_JOB_QUEUED, printJob); Loading Loading @@ -324,10 +354,9 @@ public final class PrintSpoolerService extends Service { case MSG_ON_PRINT_JOB_STATE_CHANGED: { if (mClient != null) { PrintJobId printJobId = (PrintJobId) message.obj; final int appId = message.arg1; PrintJobInfo printJob = (PrintJobInfo) message.obj; try { mClient.onPrintJobStateChanged(printJobId, appId); mClient.onPrintJobStateChanged(printJob); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error notify for print job state change.", re); } Loading Loading @@ -391,17 +420,46 @@ public final class PrintSpoolerService extends Service { public void createPrintJob(PrintJobInfo printJob) { synchronized (mLock) { addPrintJobLocked(printJob); setPrintJobState(printJob.getId(), PrintJobInfo.STATE_CREATED, null); } } private void handleReadPrintJobsLocked() { // Make a map with the files for a print job since we may have // to delete some. One example of getting orphan files if the // spooler crashes while constructing a print job. We do not // persist partially populated print jobs under construction to // avoid special handling for various attributes missing. ArrayMap<PrintJobId, File> fileForJobMap = null; File[] files = getFilesDir().listFiles(); if (files != null) { final int fileCount = files.length; for (int i = 0; i < fileCount; i++) { File file = files[i]; if (file.isFile() && file.getName().startsWith(PRINT_JOB_FILE_PREFIX)) { if (fileForJobMap == null) { fileForJobMap = new ArrayMap<PrintJobId, File>(); } String printJobIdString = file.getName().substring(0, PRINT_JOB_FILE_PREFIX.length()); PrintJobId printJobId = PrintJobId.unflattenFromString( printJobIdString); fileForJobMap.put(printJobId, file); } } } final int printJobCount = mPrintJobs.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = mPrintJobs.get(i); // We want to have only the orphan files at the end. if (fileForJobMap != null) { fileForJobMap.remove(printJob.getId()); } // Update the notification. mNotificationController.onPrintJobStateChanged(printJob); switch (printJob.getState()) { case PrintJobInfo.STATE_QUEUED: case PrintJobInfo.STATE_STARTED: Loading @@ -415,6 +473,15 @@ public final class PrintSpoolerService extends Service { } break; } } // Delete the orphan files. if (fileForJobMap != null) { final int orphanFileCount = fileForJobMap.size(); for (int i = 0; i < orphanFileCount; i++) { File file = fileForJobMap.valueAt(i); file.delete(); } } } public void checkAllPrintJobsHandled() { Loading Loading @@ -465,7 +532,7 @@ public final class PrintSpoolerService extends Service { } public File generateFileForPrintJob(PrintJobId printJobId) { return new File(getFilesDir(), "print_job_" return new File(getFilesDir(), PRINT_JOB_FILE_PREFIX + printJobId.flattenToString() + "." + PRINT_FILE_EXTENSION); } Loading @@ -476,31 +543,6 @@ public final class PrintSpoolerService extends Service { } } private void forgetPrintJobs(List<PrintJobId> printJobIds) { synchronized (mLock) { boolean printJobsRemoved = false; final int removedPrintJobCount = printJobIds.size(); for (int i = 0; i < removedPrintJobCount; i++) { PrintJobId removedPrintJobId = printJobIds.get(i); final int printJobCount = mPrintJobs.size(); for (int j = printJobCount - 1; j >= 0; j--) { PrintJobInfo printJob = mPrintJobs.get(j); if (removedPrintJobId.equals(printJob.getId())) { mPrintJobs.remove(j); printJobsRemoved = true; if (DEBUG_PRINT_JOB_LIFECYCLE) { Slog.i(LOG_TAG, "[FORGOT] " + printJob.getId().flattenToString()); } removePrintJobFileLocked(printJob.getId()); } } } if (printJobsRemoved) { mPersistanceManager.writeStateLocked(); } } } private void removeObsoletePrintJobs() { synchronized (mLock) { final int printJobCount = mPrintJobs.size(); Loading @@ -523,7 +565,7 @@ public final class PrintSpoolerService extends Service { if (file.exists()) { file.delete(); if (DEBUG_PRINT_JOB_LIFECYCLE) { Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId.flattenToString()); Slog.i(LOG_TAG, "[REMOVE FILE FOR] " + printJobId); } } } Loading Loading @@ -552,10 +594,7 @@ public final class PrintSpoolerService extends Service { switch (state) { case PrintJobInfo.STATE_COMPLETED: case PrintJobInfo.STATE_CANCELED: // Just remove the file but keep the print job info since // the app that created it may be holding onto the PrintJob // instance and query it for its most recent state. We will // remove the info for this job when told so by the system. mPrintJobs.remove(printJob); removePrintJobFileLocked(printJob.getId()); // $fall-through$ Loading @@ -582,9 +621,9 @@ public final class PrintSpoolerService extends Service { notifyOnAllPrintJobsHandled(); } Message message = mHandlerCaller.obtainMessageIIO( Message message = mHandlerCaller.obtainMessageO( HandlerCallerCallback.MSG_ON_PRINT_JOB_STATE_CHANGED, printJob.getAppId(), 0, printJob.getId()); printJob); mHandlerCaller.executeOrSendMessage(message); } } Loading
services/java/com/android/server/print/RemotePrintSpooler.java +14 −42 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.print.IPrintSpoolerCallbacks; import android.print.IPrintSpoolerClient; import android.print.PrintJobId; import android.print.PrintJobInfo; import android.print.PrintManager; import android.util.Slog; import android.util.TimedRemoteCaller; Loading Loading @@ -91,7 +90,7 @@ final class RemotePrintSpooler { public static interface PrintSpoolerCallbacks { public void onPrintJobQueued(PrintJobInfo printJob); public void onAllPrintJobsForServiceHandled(ComponentName printService); public void onPrintJobStateChanged(PrintJobId printJobId, int appId); public void onPrintJobStateChanged(PrintJobInfo printJob); } public RemotePrintSpooler(Context context, int userId, Loading Loading @@ -280,30 +279,6 @@ final class RemotePrintSpooler { } } public final void forgetPrintJobs(List<PrintJobId> printJobIds) { throwIfCalledOnMainThread(); synchronized (mLock) { throwIfDestroyedLocked(); mCanUnbind = false; } try { getRemoteInstanceLazy().forgetPrintJobs(printJobIds); } catch (RemoteException re) { Slog.e(LOG_TAG, "Error forgeting print jobs", re); } catch (TimeoutException te) { Slog.e(LOG_TAG, "Error forgeting print jobs", te); } finally { if (DEBUG) { Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] forgetPrintJobs()"); } synchronized (mLock) { mCanUnbind = true; mLock.notifyAll(); } } } public final void destroy() { throwIfCalledOnMainThread(); if (DEBUG) { Loading @@ -323,18 +298,15 @@ final class RemotePrintSpooler { .append(String.valueOf(mDestroyed)).println(); pw.append(prefix).append("bound=") .append((mRemoteInstance != null) ? "true" : "false").println(); pw.append(prefix).append("print jobs:").println(); if (mRemoteInstance != null) { List<PrintJobInfo> printJobs = getPrintJobInfos(null, PrintJobInfo.STATE_ANY, PrintManager.APP_ID_ANY); if (printJobs != null) { final int printJobCount = printJobs.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = printJobs.get(i); pw.append(prefix).append(prefix).append(printJob.toString()); pw.println(); } } pw.flush(); try { getRemoteInstanceLazy().asBinder().dump(fd, new String[]{prefix}); } catch (TimeoutException te) { /* ignore */ } catch (RemoteException re) { /* ignore */ } } } Loading @@ -346,8 +318,8 @@ final class RemotePrintSpooler { } } private void onPrintJobStateChanged(PrintJobId printJobId, int appId) { mCallbacks.onPrintJobStateChanged(printJobId, appId); private void onPrintJobStateChanged(PrintJobInfo printJob) { mCallbacks.onPrintJobStateChanged(printJob); } private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException { Loading Loading @@ -625,12 +597,12 @@ final class RemotePrintSpooler { } @Override public void onPrintJobStateChanged(PrintJobId printJobId, int appId) { public void onPrintJobStateChanged(PrintJobInfo printJob) { RemotePrintSpooler spooler = mWeakSpooler.get(); if (spooler != null) { final long identity = Binder.clearCallingIdentity(); try { spooler.onPrintJobStateChanged(printJobId, appId); spooler.onPrintJobStateChanged(printJob); } finally { Binder.restoreCallingIdentity(identity); } Loading
services/java/com/android/server/print/UserState.java +139 −41 Original line number Diff line number Diff line Loading @@ -33,7 +33,6 @@ import android.os.Looper; import android.os.Message; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.UserManager; import android.print.IPrintClient; import android.print.IPrintDocumentAdapter; import android.print.IPrintJobStateChangeListener; Loading @@ -52,6 +51,7 @@ import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import android.util.SparseArray; import com.android.internal.R; import com.android.internal.os.BackgroundThread; Loading @@ -62,6 +62,7 @@ import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.List; Loading Loading @@ -93,8 +94,8 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { private final Set<ComponentName> mEnabledServices = new ArraySet<ComponentName>(); private final CreatedPrintJobTracker mCreatedPrintJobTracker = new CreatedPrintJobTracker(); private final PrintJobForAppCache mPrintJobForAppCache = new PrintJobForAppCache(); private final Object mLock; Loading Loading @@ -155,23 +156,22 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { public PrintJobInfo print(String printJobName, final IPrintClient client, final IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId) { PrintJobId printJobId = new PrintJobId(); // Track this job so we can forget it when the creator dies. if (!mCreatedPrintJobTracker.onPrintJobCreatedLocked(client.asBinder(), printJobId)) { // Not adding a print job means the client is dead - done. return null; } // Create print job place holder. final PrintJobInfo printJob = new PrintJobInfo(); printJob.setId(printJobId); printJob.setId(new PrintJobId()); printJob.setAppId(appId); printJob.setLabel(printJobName); printJob.setAttributes(attributes); printJob.setState(PrintJobInfo.STATE_CREATED); printJob.setCopies(1); // Track this job so we can forget it when the creator dies. if (!mPrintJobForAppCache.onPrintJobCreated(client.asBinder(), appId, printJob)) { // Not adding a print job means the client is dead - done. return null; } // Spin the spooler to add the job and show the config UI. new AsyncTask<Void, Void, Void>() { @Override Loading @@ -185,10 +185,40 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } public List<PrintJobInfo> getPrintJobInfos(int appId) { return mSpooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY, appId); List<PrintJobInfo> cachedPrintJobs = mPrintJobForAppCache.getPrintJobs(appId); // Note that the print spooler is not storing print jobs that // are in a terminal state as it is non-trivial to properly update // the spooler state for when to forget print jobs in terminal state. // Therefore, we fuse the cached print jobs for running apps (some // jobs are in a terminal state) with the ones that the print // spooler knows about (some jobs are being processed). ArrayMap<PrintJobId, PrintJobInfo> result = new ArrayMap<PrintJobId, PrintJobInfo>(); // Add the cached print jobs for running apps. final int cachedPrintJobCount = cachedPrintJobs.size(); for (int i = 0; i < cachedPrintJobCount; i++) { PrintJobInfo cachedPrintJob = cachedPrintJobs.get(i); result.put(cachedPrintJob.getId(), cachedPrintJob); } // Add everything else the spooler knows about. List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY, appId); final int printJobCount = printJobs.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = printJobs.get(i); result.put(printJob.getId(), printJob); } return new ArrayList<PrintJobInfo>(result.values()); } public PrintJobInfo getPrintJobInfo(PrintJobId printJobId, int appId) { PrintJobInfo printJob = mPrintJobForAppCache.getPrintJob(printJobId, appId); if (printJob != null) { return printJob; } return mSpooler.getPrintJobInfo(printJobId, appId); } Loading Loading @@ -398,9 +428,10 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } @Override public void onPrintJobStateChanged(PrintJobId printJobId, int appId) { public void onPrintJobStateChanged(PrintJobInfo printJob) { mPrintJobForAppCache.onPrintJobStateChanged(printJob); mHandler.obtainMessage(UserStateHandler.MSG_DISPATCH_PRINT_JOB_STATE_CHANGED, appId, 0, printJobId).sendToTarget(); printJob.getAppId(), 0, printJob.getId()).sendToTarget(); } @Override Loading Loading @@ -525,6 +556,9 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { pw.println(); } pw.append(prefix).append(tab).append("cached print jobs:").println(); mPrintJobForAppCache.dump(pw, prefix + tab + tab); pw.append(prefix).append(tab).append("discovery mediator:").println(); if (mPrinterDiscoverySession != null) { mPrinterDiscoverySession.dump(pw, prefix + tab + tab); Loading Loading @@ -1424,34 +1458,19 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { } } private final class CreatedPrintJobTracker { private final ArrayMap<IBinder, List<PrintJobId>> mCreatedPrintJobs = new ArrayMap<IBinder, List<PrintJobId>>(); private final class PrintJobForAppCache { private final SparseArray<List<PrintJobInfo>> mPrintJobsForRunningApp = new SparseArray<List<PrintJobInfo>>(); public boolean onPrintJobCreatedLocked(final IBinder creator, PrintJobId printJobId) { public boolean onPrintJobCreated(final IBinder creator, final int appId, PrintJobInfo printJob) { try { creator.linkToDeath(new DeathRecipient() { @Override public void binderDied() { creator.unlinkToDeath(this, 0); UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); // If the death is a result of the user being removed, then // do nothing since the spooler data for this user will be // wiped and we cannot bind to the spooler at this point. if (userManager.getUserInfo(mUserId) == null) { return; } List<PrintJobId> printJobIds = null; synchronized (mLock) { printJobIds = mCreatedPrintJobs.remove(creator); if (printJobIds == null) { return; } printJobIds = new ArrayList<PrintJobId>(printJobIds); } if (printJobIds != null) { mSpooler.forgetPrintJobs(printJobIds); mPrintJobsForRunningApp.remove(appId); } } }, 0); Loading @@ -1460,14 +1479,93 @@ final class UserState implements PrintSpoolerCallbacks, PrintServiceCallbacks { return false; } synchronized (mLock) { List<PrintJobId> printJobIds = mCreatedPrintJobs.get(creator); if (printJobIds == null) { printJobIds = new ArrayList<PrintJobId>(); mCreatedPrintJobs.put(creator, printJobIds); List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); if (printJobsForApp == null) { printJobsForApp = new ArrayList<PrintJobInfo>(); mPrintJobsForRunningApp.put(appId, printJobsForApp); } printJobIds.add(printJobId); printJobsForApp.add(printJob); } return true; } public void onPrintJobStateChanged(PrintJobInfo printJob) { synchronized (mLock) { List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get( printJob.getAppId()); if (printJobsForApp == null) { return; } final int printJobCount = printJobsForApp.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo oldPrintJob = printJobsForApp.get(i); if (oldPrintJob.getId().equals(printJob.getId())) { printJobsForApp.set(i, printJob); } } } } public PrintJobInfo getPrintJob(PrintJobId printJobId, int appId) { synchronized (mLock) { List<PrintJobInfo> printJobsForApp = mPrintJobsForRunningApp.get(appId); if (printJobsForApp == null) { return null; } final int printJobCount = printJobsForApp.size(); for (int i = 0; i < printJobCount; i++) { PrintJobInfo printJob = printJobsForApp.get(i); if (printJob.getId().equals(printJobId)) { return printJob; } } } return null; } public List<PrintJobInfo> getPrintJobs(int appId) { synchronized (mLock) { List<PrintJobInfo> printJobs = null; if (appId == PrintManager.APP_ID_ANY) { final int bucketCount = mPrintJobsForRunningApp.size(); for (int i = 0; i < bucketCount; i++) { List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); if (printJobs == null) { printJobs = new ArrayList<PrintJobInfo>(); } printJobs.addAll(bucket); } } else { List<PrintJobInfo> bucket = mPrintJobsForRunningApp.get(appId); if (bucket != null) { if (printJobs == null) { printJobs = new ArrayList<PrintJobInfo>(); } printJobs.addAll(bucket); } } if (printJobs != null) { return printJobs; } return Collections.emptyList(); } } public void dump(PrintWriter pw, String prefix) { synchronized (mLock) { String tab = " "; final int bucketCount = mPrintJobsForRunningApp.size(); for (int i = 0; i < bucketCount; i++) { final int appId = mPrintJobsForRunningApp.keyAt(i); pw.append(prefix).append("appId=" + appId).append(':').println(); List<PrintJobInfo> bucket = mPrintJobsForRunningApp.valueAt(i); final int printJobCount = bucket.size(); for (int j = 0; j < printJobCount; j++) { PrintJobInfo printJob = bucket.get(j); pw.append(prefix).append(tab).append(printJob.toString()).println(); } } } } } }