diff options
5 files changed, 237 insertions, 129 deletions
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl index 291e81f..96b168d 100644 --- a/core/java/android/print/IPrintSpooler.aidl +++ b/core/java/android/print/IPrintSpooler.aidl @@ -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, diff --git a/core/java/android/print/IPrintSpoolerClient.aidl b/core/java/android/print/IPrintSpoolerClient.aidl index 0cf00cc..8270812 100644 --- a/core/java/android/print/IPrintSpoolerClient.aidl +++ b/core/java/android/print/IPrintSpoolerClient.aidl @@ -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); } diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java index 62b35fe..87181f7 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java @@ -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; @@ -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; @@ -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(); @@ -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( @@ -179,9 +184,6 @@ public final class PrintSpoolerService extends Service { mHandlerCaller.executeOrSendMessage(message); printJob.setCreationTime(System.currentTimeMillis()); - synchronized (mLock) { - mPersistanceManager.writeStateLocked(); - } } @Override @@ -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); @@ -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); } @@ -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: @@ -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() { @@ -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); } @@ -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(); @@ -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); } } } @@ -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$ @@ -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); } } diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java index f98a805..798cea3 100644 --- a/services/java/com/android/server/print/RemotePrintSpooler.java +++ b/services/java/com/android/server/print/RemotePrintSpooler.java @@ -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; @@ -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, @@ -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) { @@ -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 */ } } } @@ -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 { @@ -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); } diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java index e5f5842..72acc53 100644 --- a/services/java/com/android/server/print/UserState.java +++ b/services/java/com/android/server/print/UserState.java @@ -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; @@ -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; @@ -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; @@ -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; @@ -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 @@ -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); } @@ -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 @@ -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); @@ -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); @@ -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(); + } + } + } + } } } |