diff options
27 files changed, 789 insertions, 198 deletions
diff --git a/api/current.txt b/api/current.txt index 0418cd9..f3c4ac9 100644 --- a/api/current.txt +++ b/api/current.txt @@ -25,6 +25,7 @@ package android { field public static final java.lang.String BIND_NFC_SERVICE = "android.permission.BIND_NFC_SERVICE"; field public static final java.lang.String BIND_NOTIFICATION_LISTENER_SERVICE = "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"; field public static final java.lang.String BIND_PRINT_SERVICE = "android.permission.BIND_PRINT_SERVICE"; + field public static final java.lang.String BIND_PRINT_SPOOLER_SERVICE = "android.permission.BIND_PRINT_SPOOLER_SERVICE"; field public static final java.lang.String BIND_REMOTEVIEWS = "android.permission.BIND_REMOTEVIEWS"; field public static final java.lang.String BIND_TEXT_SERVICE = "android.permission.BIND_TEXT_SERVICE"; field public static final java.lang.String BIND_VPN_SERVICE = "android.permission.BIND_VPN_SERVICE"; @@ -19142,7 +19143,7 @@ package android.print { method public void onFinish(); method public abstract void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle); method public void onStart(); - method public abstract void onWrite(android.print.PageRange[], java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback); + method public abstract void onWrite(android.print.PageRange[], android.os.ParcelFileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback); field public static final java.lang.String METADATA_KEY_PRINT_PREVIEW = "KEY_METADATA_PRINT_PREVIEW"; } @@ -19162,6 +19163,7 @@ package android.print { method public int describeContents(); method public int getColorMode(); method public int getContentType(); + method public long getDataSize(); method public int getFittingMode(); method public android.print.PrintAttributes.Margins getMargins(); method public android.print.PrintAttributes.MediaSize getMediaSize(); @@ -19193,7 +19195,7 @@ package android.print { public class PrintFileDocumentAdapter extends android.print.PrintDocumentAdapter { ctor public PrintFileDocumentAdapter(android.content.Context, java.io.File, android.print.PrintDocumentInfo); method public void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback, android.os.Bundle); - method public void onWrite(android.print.PageRange[], java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback); + method public void onWrite(android.print.PageRange[], android.os.ParcelFileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback); } public final class PrintJob { @@ -19215,9 +19217,10 @@ package android.print { method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final int PRINT_JOB_ID_UNDEFINED = -1; // 0xffffffff - field public static final int STATE_CANCELED = 6; // 0x6 - field public static final int STATE_COMPLETED = 4; // 0x4 - field public static final int STATE_FAILED = 5; // 0x5 + field public static final int STATE_BLOCKED = 4; // 0x4 + field public static final int STATE_CANCELED = 7; // 0x7 + field public static final int STATE_COMPLETED = 5; // 0x5 + field public static final int STATE_FAILED = 6; // 0x6 field public static final int STATE_QUEUED = 2; // 0x2 field public static final int STATE_STARTED = 3; // 0x3 } @@ -19335,17 +19338,19 @@ package android.print.pdf { package android.printservice { public final class PrintDocument { - method public java.io.FileDescriptor getData(); + method public android.os.ParcelFileDescriptor getData(); method public android.print.PrintDocumentInfo getInfo(); } public final class PrintJob { + method public boolean block(java.lang.String); method public boolean cancel(); method public boolean complete(); method public boolean fail(java.lang.String); method public android.printservice.PrintDocument getDocument(); method public int getId(); method public android.print.PrintJobInfo getInfo(); + method public boolean isBlocked(); method public boolean isCancelled(); method public boolean isCompleted(); method public boolean isFailed(); @@ -19377,9 +19382,11 @@ package android.printservice { method public final boolean isDestroyed(); method public final boolean isPrinterDiscoveryStarted(); method public abstract void onDestroy(); - method public abstract void onRequestPrinterUpdate(android.print.PrinterId); method public abstract void onStartPrinterDiscovery(java.util.List<android.print.PrinterId>); + method public abstract void onStartPrinterStateTracking(android.print.PrinterId); method public abstract void onStopPrinterDiscovery(); + method public abstract void onStopPrinterStateTracking(android.print.PrinterId); + method public abstract void onValidatePrinters(java.util.List<android.print.PrinterId>); method public final void removePrinters(java.util.List<android.print.PrinterId>); method public final void updatePrinters(java.util.List<android.print.PrinterInfo>); } diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl index 3bfd9a1..fb6bb2e 100644 --- a/core/java/android/print/IPrintManager.aidl +++ b/core/java/android/print/IPrintManager.aidl @@ -41,7 +41,9 @@ interface IPrintManager { void startPrinterDiscovery(in IPrinterDiscoveryObserver observer, in List<PrinterId> priorityList, int userId); void stopPrinterDiscovery(in IPrinterDiscoveryObserver observer, int userId); - void requestPrinterUpdate(in PrinterId printerId, int userId); + void validatePrinters(in List<PrinterId> printerIds, int userId); + void startPrinterStateTracking(in PrinterId printerId, int userId); + void stopPrinterStateTracking(in PrinterId printerId, int userId); void destroyPrinterDiscoverySession(in IPrinterDiscoveryObserver observer, int userId); } diff --git a/core/java/android/print/PrintDocumentAdapter.java b/core/java/android/print/PrintDocumentAdapter.java index 8a64e85..33b4aad 100644 --- a/core/java/android/print/PrintDocumentAdapter.java +++ b/core/java/android/print/PrintDocumentAdapter.java @@ -18,8 +18,8 @@ package android.print; import android.os.Bundle; import android.os.CancellationSignal; +import android.os.ParcelFileDescriptor; -import java.io.FileDescriptor; import java.util.List; /** @@ -41,7 +41,7 @@ import java.util.List; * <li> * After every call to {@link #onLayout(PrintAttributes, PrintAttributes, * CancellationSignal, LayoutResultCallback, Bundle)}, you may get a call to - * {@link #onWrite(PageRange[], FileDescriptor, CancellationSignal, WriteResultCallback)} + * {@link #onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, WriteResultCallback)} * asking you to write a PDF file with the content for specific pages. * </li> * <li> @@ -64,7 +64,7 @@ import java.util.List; * PrintAttributes, CancellationSignal, LayoutResultCallback, Bundle)} on * the UI thread (assuming onStart initializes resources needed for layout). * This will ensure that the UI does not change while you are laying out the - * printed content. Then you can handle {@link #onWrite(PageRange[], FileDescriptor, + * printed content. Then you can handle {@link #onWrite(PageRange[], ParcelFileDescriptor, * CancellationSignal, WriteResultCallback)} and {@link #onFinish()} on another * thread. This will ensure that the UI is frozen for the minimal amount of * time. Also this assumes that you will generate the printed content in @@ -150,10 +150,10 @@ public abstract class PrintDocumentAdapter { * from of a PDF file to the given file descriptor. This method is invoked * on the main thread. *<p> - * After you are done writing, you should <strong>not</strong> close the - * file descriptor, rather you must invoke: {@link WriteResultCallback - * #onWriteFinished(List)}, if writing completed successfully; or {@link - * WriteResultCallback#onWriteFailed(CharSequence)}, if an error occurred. + * After you are done writing, you should close the file descriptor and + * invoke {@link WriteResultCallback #onWriteFinished(List)}, if writing + * completed successfully; or {@link WriteResultCallback#onWriteFailed( + * CharSequence)}, if an error occurred. * </p> * <p> * <strong>Note:</strong> If the printed content is large, it is a good @@ -171,7 +171,7 @@ public abstract class PrintDocumentAdapter { * @see WriteResultCallback * @see CancellationSignal */ - public abstract void onWrite(PageRange[] pages, FileDescriptor destination, + public abstract void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback); /** @@ -185,7 +185,7 @@ public abstract class PrintDocumentAdapter { /** * Base class for implementing a callback for the result of {@link - * PrintDocumentAdapter#onWrite(PageRange[], FileDescriptor, CancellationSignal, + * PrintDocumentAdapter#onWrite(PageRange[], ParcelFileDescriptor, CancellationSignal, * WriteResultCallback)}. */ public static abstract class WriteResultCallback { diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java index b32961b..f2b91ae 100644 --- a/core/java/android/print/PrintDocumentInfo.java +++ b/core/java/android/print/PrintDocumentInfo.java @@ -60,6 +60,7 @@ public final class PrintDocumentInfo implements Parcelable { private int mColorMode; private Margins mMargins; private MediaSize mMediaSize; + private long mDataSize; /** * Creates a new instance. @@ -82,6 +83,7 @@ public final class PrintDocumentInfo implements Parcelable { mColorMode = prototype.mColorMode; mMargins = prototype.mMargins; mMediaSize = prototype.mMediaSize; + mDataSize = prototype.mDataSize; } /** @@ -98,6 +100,7 @@ public final class PrintDocumentInfo implements Parcelable { mColorMode = parcel.readInt(); mMargins = Margins.createFromParcel(parcel); mMediaSize = MediaSize.createFromParcel(parcel); + mDataSize = parcel.readLong(); } /** @@ -188,6 +191,26 @@ public final class PrintDocumentInfo implements Parcelable { return mMediaSize; } + /** + * Gets the document data size in bytes. + * + * @return The data size. + */ + public long getDataSize() { + return mDataSize; + } + + /** + * Sets the document data size in bytes. + * + * @param dataSize The data size. + * + * @hide + */ + public void setDataSize(long dataSize) { + mDataSize = dataSize; + } + @Override public int describeContents() { return 0; @@ -203,6 +226,7 @@ public final class PrintDocumentInfo implements Parcelable { parcel.writeInt(mColorMode); mMargins.writeToParcel(parcel); mMediaSize.writeToParcel(parcel); + parcel.writeLong(mDataSize); } @Override @@ -217,6 +241,8 @@ public final class PrintDocumentInfo implements Parcelable { result = prime * result + mColorMode; result = prime * result + (mMargins != null ? mMargins.hashCode() : 0); result = prime * result + (mMediaSize != null ? mMediaSize.hashCode() : 0); + result = prime * result + (int) mDataSize; + result = prime * result + (int) mDataSize >> 32; return result; } @@ -264,6 +290,9 @@ public final class PrintDocumentInfo implements Parcelable { } else if (!mMediaSize.equals(other.mMediaSize)) { return false; } + if (mDataSize != other.mDataSize) { + return false; + } return true; } @@ -279,6 +308,7 @@ public final class PrintDocumentInfo implements Parcelable { builder.append(", colorMode=").append(PrintAttributes.colorModeToString(mColorMode)); builder.append(", margins=").append(mMargins); builder.append(", mediaSize=").append(mMediaSize); + builder.append(", size=").append(mDataSize); builder.append("}"); return builder.toString(); } diff --git a/core/java/android/print/PrintFileDocumentAdapter.java b/core/java/android/print/PrintFileDocumentAdapter.java index dbc8b6f..b905396 100644 --- a/core/java/android/print/PrintFileDocumentAdapter.java +++ b/core/java/android/print/PrintFileDocumentAdapter.java @@ -21,6 +21,7 @@ import android.os.AsyncTask; import android.os.Bundle; import android.os.CancellationSignal; import android.os.CancellationSignal.OnCancelListener; +import android.os.ParcelFileDescriptor; import android.util.Log; import com.android.internal.R; @@ -28,7 +29,6 @@ import com.android.internal.R; import libcore.io.IoUtils; import java.io.File; -import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; @@ -81,7 +81,7 @@ public class PrintFileDocumentAdapter extends PrintDocumentAdapter { } @Override - public void onWrite(PageRange[] pages, FileDescriptor destination, + public void onWrite(PageRange[] pages, ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback) { mWriteFileAsyncTask = new WriteFileAsyncTask(destination, cancellationSignal, callback); mWriteFileAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, @@ -90,13 +90,13 @@ public class PrintFileDocumentAdapter extends PrintDocumentAdapter { private final class WriteFileAsyncTask extends AsyncTask<Void, Void, Void> { - private final FileDescriptor mDestination; + private final ParcelFileDescriptor mDestination; private final WriteResultCallback mResultCallback; private final CancellationSignal mCancellationSignal; - public WriteFileAsyncTask(FileDescriptor destination, + public WriteFileAsyncTask(ParcelFileDescriptor destination, CancellationSignal cancellationSignal, WriteResultCallback callback) { mDestination = destination; mResultCallback = callback; @@ -112,7 +112,7 @@ public class PrintFileDocumentAdapter extends PrintDocumentAdapter { @Override protected Void doInBackground(Void... params) { InputStream in = null; - OutputStream out = new FileOutputStream(mDestination); + OutputStream out = new FileOutputStream(mDestination.getFileDescriptor()); final byte[] buffer = new byte[8192]; try { in = new FileInputStream(mFile); diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java index 602f3c1..b919ad6 100644 --- a/core/java/android/print/PrintJobInfo.java +++ b/core/java/android/print/PrintJobInfo.java @@ -44,6 +44,13 @@ public final class PrintJobInfo implements Parcelable { public static final int STATE_ANY_VISIBLE_TO_CLIENTS = -2; /** + * Constant for matching any active print job state. + * + * @hide + */ + public static final int STATE_ANY_ACTIVE = -3; + + /** * Print job state: The print job is being created but not yet * ready to be printed. * <p> @@ -55,7 +62,7 @@ public final class PrintJobInfo implements Parcelable { public static final int STATE_CREATED = 1; /** - * Print job status: The print jobs is created, it is ready + * Print job state: The print jobs is created, it is ready * to be printed and should be processed. * <p> * Next valid states: {@link #STATE_STARTED}, {@link #STATE_FAILED}, @@ -65,40 +72,49 @@ public final class PrintJobInfo implements Parcelable { public static final int STATE_QUEUED = 2; /** - * Print job status: The print job is being printed. + * Print job state: The print job is being printed. * <p> * Next valid states: {@link #STATE_COMPLETED}, {@link #STATE_FAILED}, - * {@link #STATE_CANCELED} + * {@link #STATE_CANCELED}, {@link #STATE_BLOCKED} * </p> */ public static final int STATE_STARTED = 3; /** - * Print job status: The print job was successfully printed. + * Print job state: The print job is blocked. + * <p> + * Next valid states: {@link #STATE_FAILED}, {@link #STATE_CANCELED}, + * {@link #STATE_STARTED} + * </p> + */ + public static final int STATE_BLOCKED = 4; + + /** + * Print job state: The print job was successfully printed. * This is a terminal state. * <p> * Next valid states: None * </p> */ - public static final int STATE_COMPLETED = 4; + public static final int STATE_COMPLETED = 5; /** - * Print job status: The print job was printing but printing failed. + * Print job state: The print job was printing but printing failed. * This is a terminal state. * <p> * Next valid states: None * </p> */ - public static final int STATE_FAILED = 5; + public static final int STATE_FAILED = 6; /** - * Print job status: The print job was canceled. + * Print job state: The print job was canceled. * This is a terminal state. * <p> * Next valid states: None * </p> */ - public static final int STATE_CANCELED = 6; + public static final int STATE_CANCELED = 7; /** The unique print job id. */ private int mId; @@ -127,8 +143,8 @@ public final class PrintJobInfo implements Parcelable { /** How many copies to print. */ private int mCopies; - /** Failure reason if this job failed. */ - private String mFailureReason; + /** Reason for the print job being in its current state. */ + private String mStateReason; /** The pages to print */ private PageRange[] mPageRanges; @@ -155,7 +171,7 @@ public final class PrintJobInfo implements Parcelable { mUserId = other.mUserId; mTag = other.mTag; mCopies = other.mCopies; - mFailureReason = other.mFailureReason; + mStateReason = other.mStateReason; mPageRanges = other.mPageRanges; mAttributes = other.mAttributes; mDocumentInfo = other.mDocumentInfo; @@ -171,7 +187,7 @@ public final class PrintJobInfo implements Parcelable { mUserId = parcel.readInt(); mTag = parcel.readString(); mCopies = parcel.readInt(); - mFailureReason = parcel.readString(); + mStateReason = parcel.readString(); if (parcel.readInt() == 1) { Parcelable[] parcelables = parcel.readParcelableArray(null); mPageRanges = new PageRange[parcelables.length]; @@ -377,25 +393,27 @@ public final class PrintJobInfo implements Parcelable { } /** - * The failure reason if this print job failed. + * Gets the reason for the print job being in the current state. * - * @return The failure reason. + * @return The reason, or null if there is no reason or the + * reason is unknown. * * @hide */ - public String getFailureReason() { - return mFailureReason; + public String getStateReason() { + return mStateReason; } /** - * The failure reason if this print job failed. + * Sets the reason for the print job being in the current state. * - * @param failureReason The failure reason. + * @param stateReason The reason, or null if there is no reason + * or the reason is unknown. * * @hide */ - public void setFailureReason(String failureReason) { - mFailureReason = failureReason; + public void setStateReason(String stateReason) { + mStateReason = stateReason; } /** @@ -476,7 +494,7 @@ public final class PrintJobInfo implements Parcelable { parcel.writeInt(mUserId); parcel.writeString(mTag); parcel.writeInt(mCopies); - parcel.writeString(mFailureReason); + parcel.writeString(mStateReason); if (mPageRanges != null) { parcel.writeInt(1); parcel.writeParcelableArray(mPageRanges, flags); diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java index d3e35c3..6e32c05 100644 --- a/core/java/android/print/PrintManager.java +++ b/core/java/android/print/PrintManager.java @@ -36,7 +36,6 @@ import com.android.internal.os.SomeArgs; import libcore.io.IoUtils; import java.io.File; -import java.io.FileDescriptor; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collections; @@ -163,7 +162,7 @@ public final class PrintManager { * @param pdfFile The PDF file to print. * @param documentInfo Information about the printed document. * @param attributes The default print job attributes. - * @return The created print job. + * @return The created print job on success or null on failure. * * @see PrintJob */ @@ -181,7 +180,7 @@ public final class PrintManager { * @param printJobName A name for the new print job. * @param documentAdapter An adapter that emits the document to print. * @param attributes The default print job attributes. - * @return The created print job. + * @return The created print job on success or null on failure. * * @see PrintJob */ @@ -279,7 +278,7 @@ public final class PrintManager { } SomeArgs args = SomeArgs.obtain(); args.arg1 = pages; - args.arg2 = fd.getFileDescriptor(); + args.arg2 = fd; args.arg3 = callback; args.argi1 = sequence; mHandler.removeMessages(MyHandler.MSG_WRITE); @@ -342,7 +341,7 @@ public final class PrintManager { case MSG_WRITE: { SomeArgs args = (SomeArgs) message.obj; PageRange[] pages = (PageRange[]) args.arg1; - FileDescriptor fd = (FileDescriptor) args.arg2; + ParcelFileDescriptor fd = (ParcelFileDescriptor) args.arg2; IWriteResultCallback callback = (IWriteResultCallback) args.arg3; final int sequence = args.argi1; args.recycle(); @@ -428,12 +427,12 @@ public final class PrintManager { } private final class MyWriteResultCallback extends WriteResultCallback { - private FileDescriptor mFd; + private ParcelFileDescriptor mFd; private int mSequence; private IWriteResultCallback mCallback; public MyWriteResultCallback(IWriteResultCallback callback, - FileDescriptor fd, int sequence) { + ParcelFileDescriptor fd, int sequence) { mFd = fd; mSequence = sequence; mCallback = callback; diff --git a/core/java/android/print/PrinterDiscoverySession.java b/core/java/android/print/PrinterDiscoverySession.java index 8fbdd9c..46f0bef 100644 --- a/core/java/android/print/PrinterDiscoverySession.java +++ b/core/java/android/print/PrinterDiscoverySession.java @@ -74,6 +74,7 @@ public final class PrinterDiscoverySession { public final void startPrinterDisovery(List<PrinterId> priorityList) { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring start printers dsicovery - session destroyed"); + return; } if (!mIsPrinterDiscoveryStarted) { mIsPrinterDiscoveryStarted = true; @@ -88,6 +89,7 @@ public final class PrinterDiscoverySession { public final void stopPrinterDiscovery() { if (isDestroyed()) { Log.w(LOG_TAG, "Ignoring stop printers discovery - session destroyed"); + return; } if (mIsPrinterDiscoveryStarted) { mIsPrinterDiscoveryStarted = false; @@ -99,14 +101,39 @@ public final class PrinterDiscoverySession { } } - public final void requestPrinterUpdate(PrinterId printerId) { + public final void startPrinterStateTracking(PrinterId printerId) { + if (isDestroyed()) { + Log.w(LOG_TAG, "Ignoring start printer state tracking - session destroyed"); + return; + } + try { + mPrintManager.startPrinterStateTracking(printerId, mUserId); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error starting printer state tracking", re); + } + } + + public final void stopPrinterStateTracking(PrinterId printerId) { if (isDestroyed()) { - Log.w(LOG_TAG, "Ignoring reqeust printer update - session destroyed"); + Log.w(LOG_TAG, "Ignoring stop printer state tracking - session destroyed"); + return; + } + try { + mPrintManager.stopPrinterStateTracking(printerId, mUserId); + } catch (RemoteException re) { + Log.e(LOG_TAG, "Error stoping printer state tracking", re); + } + } + + public final void validatePrinters(List<PrinterId> printerIds) { + if (isDestroyed()) { + Log.w(LOG_TAG, "Ignoring validate printers - session destroyed"); + return; } try { - mPrintManager.requestPrinterUpdate(printerId, mUserId); + mPrintManager.validatePrinters(printerIds, mUserId); } catch (RemoteException re) { - Log.e(LOG_TAG, "Error requesting printer update", re); + Log.e(LOG_TAG, "Error validating printers", re); } } diff --git a/core/java/android/print/pdf/PrintedPdfDocument.java b/core/java/android/print/pdf/PrintedPdfDocument.java index a3be38b..bee17ef 100644 --- a/core/java/android/print/pdf/PrintedPdfDocument.java +++ b/core/java/android/print/pdf/PrintedPdfDocument.java @@ -111,7 +111,10 @@ public final class PrintedPdfDocument { * @see #finishPage(Page) */ public Page startPage(int pageNumber) { - PageInfo pageInfo = new PageInfo.Builder(mPageSize, 0).create(); + PageInfo pageInfo = new PageInfo + .Builder(mPageSize, 0) + .setContentSize(mContentSize) + .create(); Page page = mDocument.startPage(pageInfo); return page; } diff --git a/core/java/android/printservice/IPrintService.aidl b/core/java/android/printservice/IPrintService.aidl index 2cee1d8..ee36619 100644 --- a/core/java/android/printservice/IPrintService.aidl +++ b/core/java/android/printservice/IPrintService.aidl @@ -33,6 +33,8 @@ oneway interface IPrintService { void createPrinterDiscoverySession(); void startPrinterDiscovery(in List<PrinterId> priorityList); void stopPrinterDiscovery(); - void requestPrinterUpdate(in PrinterId printerId); + void validatePrinters(in List<PrinterId> printerIds); + void startPrinterStateTracking(in PrinterId printerId); + void stopPrinterStateTracking(in PrinterId printerId); void destroyPrinterDiscoverySession(); } diff --git a/core/java/android/printservice/PrintDocument.java b/core/java/android/printservice/PrintDocument.java index 7437dc5..8292cfb 100644 --- a/core/java/android/printservice/PrintDocument.java +++ b/core/java/android/printservice/PrintDocument.java @@ -21,12 +21,15 @@ import android.os.RemoteException; import android.print.PrintDocumentInfo; import android.util.Log; -import java.io.FileDescriptor; import java.io.IOException; /** * This class represents a printed document from the perspective of a print * service. It exposes APIs to query the document and obtain its data. + * <p> + * <strong>Note: </strong> All methods of this class must be executed on the + * main application thread. + * </p> */ public final class PrintDocument { @@ -51,6 +54,7 @@ public final class PrintDocument { * @return The document info. */ public PrintDocumentInfo getInfo() { + PrintService.throwIfNotCalledOnMainThread(); return mInfo; } @@ -64,7 +68,8 @@ public final class PrintDocument { * * @return A file descriptor for reading the data. */ - public FileDescriptor getData() { + public ParcelFileDescriptor getData() { + PrintService.throwIfNotCalledOnMainThread(); ParcelFileDescriptor source = null; ParcelFileDescriptor sink = null; try { @@ -72,7 +77,7 @@ public final class PrintDocument { source = fds[0]; sink = fds[1]; mPrintServiceClient.writePrintJobData(sink, mPrintJobId); - return source.getFileDescriptor(); + return source; } catch (IOException ioe) { Log.e(LOG_TAG, "Error calling getting print job data!", ioe); } catch (RemoteException re) { diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java index d2fbef2..8bae9d6 100644 --- a/core/java/android/printservice/PrintJob.java +++ b/core/java/android/printservice/PrintJob.java @@ -18,6 +18,7 @@ package android.printservice; import android.os.RemoteException; import android.print.PrintJobInfo; +import android.text.TextUtils; import android.util.Log; /** @@ -123,6 +124,21 @@ public final class PrintJob { } /** + * Gets whether this print job is blocked. Such a print job is halted + * due to an abnormal condition and can be started or canceled or failed. + * + * @return Whether the print job is blocked. + * + * @see #start() + * @see #cancel() + * @see #fail(CharSequence) + */ + public boolean isBlocked() { + PrintService.throwIfNotCalledOnMainThread(); + return getInfo().getState() == PrintJobInfo.STATE_BLOCKED; + } + + /** * Gets whether this print job is completed. Such a print job * is successfully printed. This is a final state. * @@ -163,21 +179,49 @@ public final class PrintJob { /** * Starts the print job. You should call this method if {@link - * #isQueued()} returns true and you started printing. + * #isQueued()} or {@link #isBlocked()} returns true and you started + * resumed printing. * - * @return Whether the job as started. + * @return Whether the job was started. * * @see #isQueued() + * @see #isBlocked() */ public boolean start() { PrintService.throwIfNotCalledOnMainThread(); - if (isQueued()) { + final int state = getInfo().getState(); + if (state == PrintJobInfo.STATE_QUEUED + || state == PrintJobInfo.STATE_BLOCKED) { return setState(PrintJobInfo.STATE_STARTED, null); } return false; } /** + * Blocks the print job. You should call this method if {@link + * #isStarted()} or {@link #isBlocked()} returns true and you need + * to block the print job. For example, the user has to add some + * paper to continue printing. To resume the print job call {@link + * #start()}. + * + * @return Whether the job was blocked. + * + * @see #isStarted() + * @see #isBlocked() + */ + public boolean block(String reason) { + PrintService.throwIfNotCalledOnMainThread(); + PrintJobInfo info = getInfo(); + final int state = info.getState(); + if (state == PrintJobInfo.STATE_STARTED + || (state == PrintJobInfo.STATE_BLOCKED + && !TextUtils.equals(info.getStateReason(), reason))) { + return setState(PrintJobInfo.STATE_BLOCKED, reason); + } + return false; + } + + /** * Completes the print job. You should call this method if {@link * #isStarted()} returns true and you are done printing. * @@ -195,8 +239,8 @@ public final class PrintJob { /** * Fails the print job. You should call this method if {@link - * #isQueued()} or {@link #isStarted()} returns true you failed - * while printing. + * #isQueued()} or {@link #isStarted()} or {@link #isBlocked()} + * returns true you failed while printing. * * @param error The human readable, short, and translated reason * for the failure. @@ -204,10 +248,11 @@ public final class PrintJob { * * @see #isQueued() * @see #isStarted() + * @see #isBlocked() */ public boolean fail(String error) { PrintService.throwIfNotCalledOnMainThread(); - if (isQueued() || isStarted()) { + if (!isInImmutableState()) { return setState(PrintJobInfo.STATE_FAILED, error); } return false; @@ -215,18 +260,19 @@ public final class PrintJob { /** * Cancels the print job. You should call this method if {@link - * #isQueued()} or {@link #isStarted()} returns true and you canceled - * the print job as a response to a call to {@link - * PrintService#onRequestCancelPrintJob(PrintJob)}. + * #isQueued()} or {@link #isStarted() or #isBlocked()} returns + * true and you canceled the print job as a response to a call to + * {@link PrintService#onRequestCancelPrintJob(PrintJob)}. * * @return Whether the job is canceled. * * @see #isStarted() * @see #isQueued() + * @see #isBlocked() */ public boolean cancel() { PrintService.throwIfNotCalledOnMainThread(); - if (isQueued() || isStarted()) { + if (!isInImmutableState()) { return setState(PrintJobInfo.STATE_CANCELED, null); } return false; @@ -277,7 +323,8 @@ public final class PrintJob { private boolean isInImmutableState() { final int state = mCachedInfo.getState(); return state == PrintJobInfo.STATE_COMPLETED - || state == PrintJobInfo.STATE_CANCELED; + || state == PrintJobInfo.STATE_CANCELED + || state == PrintJobInfo.STATE_FAILED; } private boolean setState(int state, String error) { @@ -287,7 +334,7 @@ public final class PrintJob { // we may not be able to re-fetch it later if the job gets // removed from the spooler as a result of the state change. mCachedInfo.setState(state); - mCachedInfo.setFailureReason(error); + mCachedInfo.setStateReason(error); return true; } } catch (RemoteException re) { diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java index f6c0a9a..96552af 100644 --- a/core/java/android/printservice/PrintService.java +++ b/core/java/android/printservice/PrintService.java @@ -314,8 +314,20 @@ public abstract class PrintService extends Service { } @Override - public void requestPrinterUpdate(PrinterId printerId) { - mHandler.obtainMessage(ServiceHandler.MSG_REQUEST_PRINTER_UPDATE, + public void validatePrinters(List<PrinterId> printerIds) { + mHandler.obtainMessage(ServiceHandler.MSG_VALIDATE_PRINTERS, + printerIds).sendToTarget(); + } + + @Override + public void startPrinterStateTracking(PrinterId printerId) { + mHandler.obtainMessage(ServiceHandler.MSG_START_PRINTER_STATE_TRACKING, + printerId).sendToTarget(); + } + + @Override + public void stopPrinterStateTracking(PrinterId printerId) { + mHandler.obtainMessage(ServiceHandler.MSG_STOP_PRINTER_STATE_TRACKING, printerId).sendToTarget(); } @@ -344,10 +356,12 @@ public abstract class PrintService extends Service { public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2; public static final int MSG_START_PRINTER_DISCOVERY = 3; public static final int MSG_STOP_PRINTER_DISCOVERY = 4; - public static final int MSG_REQUEST_PRINTER_UPDATE = 5; - public static final int MSG_ON_PRINTJOB_QUEUED = 6; - public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 7; - public static final int MSG_SET_CLEINT = 8; + public static final int MSG_VALIDATE_PRINTERS = 5; + public static final int MSG_START_PRINTER_STATE_TRACKING = 6; + public static final int MSG_STOP_PRINTER_STATE_TRACKING = 7; + public static final int MSG_ON_PRINTJOB_QUEUED = 8; + public static final int MSG_ON_REQUEST_CANCEL_PRINTJOB = 9; + public static final int MSG_SET_CLEINT = 10; public ServiceHandler(Looper looper) { super(looper, null, true); @@ -391,10 +405,24 @@ public abstract class PrintService extends Service { } } break; - case MSG_REQUEST_PRINTER_UPDATE: { + case MSG_VALIDATE_PRINTERS: { + if (mDiscoverySession != null) { + List<PrinterId> printerIds = (List<PrinterId>) message.obj; + mDiscoverySession.validatePrinters(printerIds); + } + } break; + + case MSG_START_PRINTER_STATE_TRACKING: { + if (mDiscoverySession != null) { + PrinterId printerId = (PrinterId) message.obj; + mDiscoverySession.startPrinterStateTracking(printerId); + } + } break; + + case MSG_STOP_PRINTER_STATE_TRACKING: { if (mDiscoverySession != null) { PrinterId printerId = (PrinterId) message.obj; - mDiscoverySession.requestPrinterUpdate(printerId); + mDiscoverySession.stopPrinterStateTracking(printerId); } } break; diff --git a/core/java/android/printservice/PrinterDiscoverySession.java b/core/java/android/printservice/PrinterDiscoverySession.java index 8b959a6..1f86ecc 100644 --- a/core/java/android/printservice/PrinterDiscoverySession.java +++ b/core/java/android/printservice/PrinterDiscoverySession.java @@ -53,15 +53,23 @@ import java.util.List; * session. Printers are <strong>not</strong> persisted across sessions. * </p> * <p> - * The system will make a call to - * {@link PrinterDiscoverySession#onRequestPrinterUpdate(PrinterId)} if you - * need to update a given printer. It is possible that you add a printer without + * The system will make a call to {@link #onValidatePrinters(List)} if you + * need to update some printers. It is possible that you add a printer without * specifying its capabilities. This enables you to avoid querying all discovered * printers for their capabilities, rather querying the capabilities of a printer * only if necessary. For example, the system will request that you update a printer - * if it gets selected by the user. If you did not report the printer capabilities - * when adding it, you must do so after the system requests a printer update. - * Otherwise, the printer will be ignored. + * if it gets selected by the user. When validating printers you do not need to + * provide the printers' capabilities but may do so. + * </p> + * <p> + * If the system is interested in being constantly updated for the state of a + * printer you will receive a call to {@link #onStartPrinterStateTracking(PrinterId)} + * after which you will have to do a best effort to keep the system updated for + * changes in the printer state and capabilities. You also <strong>must</strong> + * update the printer capabilities if you did not provide them when adding it, or + * the printer will be ignored. When the system is no longer interested in getting + * updates for a printer you will receive a call to {@link #onStopPrinterStateTracking( + * PrinterId)}. * </p> * <p> * <strong>Note: </strong> All callbacks in this class are executed on the main @@ -115,7 +123,7 @@ public abstract class PrinterDiscoverySession { * the printer that was added but not removed. * <p> * <strong>Note: </strong> Calls to this method after the session is - * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored. + * destroyed, that is after the {@link #onDestroy()} callback, will be ignored. * </p> * * @return The printers. @@ -139,7 +147,7 @@ public abstract class PrinterDiscoverySession { * times during the life of this session. Duplicates will be ignored. * <p> * <strong>Note: </strong> Calls to this method after the session is - * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored. + * destroyed, that is after the {@link #onDestroy()} callback, will be ignored. * </p> * * @param printers The printers to add. @@ -218,7 +226,7 @@ public abstract class PrinterDiscoverySession { * call this method multiple times during the lifetime of this session. * <p> * <strong>Note: </strong> Calls to this method after the session is - * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored. + * destroyed, that is after the {@link #onDestroy()} callback, will be ignored. * </p> * * @param printerIds The ids of the removed printers. @@ -293,7 +301,7 @@ public abstract class PrinterDiscoverySession { * during the lifetime of this session. * <p> * <strong>Note: </strong> Calls to this method after the session is - * destroyed, i.e. after the {@link #onDestroy()} callback, will be ignored. + * destroyed, that is after the {@link #onDestroy()} callback, will be ignored. * </p> * * @param printers The printers to update. @@ -441,7 +449,9 @@ public abstract class PrinterDiscoverySession { * <p> * <strong>Note: </strong>You are also given a list of printers whose availability * has to be checked first. For example, these printers could be the user's favorite - * ones, therefore they have to be verified first. + * ones, therefore they have to be verified first. You do <strong>not need</strong> + * to provide the capabilities of the printers, rather verify whether they exist + * similarly to {@link #onValidatePrinters(List)}. * </p> * * @param priorityList The list of printers to validate first. Never null. @@ -463,9 +473,28 @@ public abstract class PrinterDiscoverySession { public abstract void onStopPrinterDiscovery(); /** - * Requests that you update a printer. You are responsible for updating - * the printer by also reporting its capabilities via calling {@link - * #updatePrinters(List)}. + * Callback asking you to validate that the given printers are valid, that + * is they exist. You are responsible for checking whether these printers + * exist and for the ones that do exist notify the system via calling + * {@link #updatePrinters(List)}. + * <p> + * <strong>Note: </strong> You are <strong>not required</strong> to provide + * the printer capabilities when updating the printers that do exist. + * <p> + * + * @param printerIds The printers to validate. + * + * @see #updatePrinters(List) + * @see PrinterInfo.Builder#setCapabilities(PrinterCapabilitiesInfo) + * PrinterInfo.Builder.setCapabilities(PrinterCapabilitiesInfo) + */ + public abstract void onValidatePrinters(List<PrinterId> printerIds); + + /** + * Callback asking you to start tracking the state of a printer. Tracking + * the state means that you should do a best effort to observe the state + * of this printer and notify the system if that state changes via calling + * {@link #updatePrinters(List)}. * <p> * <strong>Note: </strong> A printer can be initially added without its * capabilities to avoid polling printers that the user will not select. @@ -473,18 +502,33 @@ public abstract class PrinterDiscoverySession { * printer <strong>including</strong> its capabilities. Otherwise, the * printer will be ignored. * <p> - * A scenario when you may be requested to update a printer is if the user - * selects it and the system has to present print options UI based on the - * printer's capabilities. + * <p> + * A scenario when you may be requested to track a printer's state is if + * the user selects that printer and the system has to present print + * options UI based on the printer's capabilities. In this case the user + * should be promptly informed if, for example, the printer becomes + * unavailable. * </p> * - * @param printerId The printer id. + * @param printerId The printer to start tracking. * + * @see #onStopPrinterStateTracking(PrinterId) * @see #updatePrinters(List) * @see PrinterInfo.Builder#setCapabilities(PrinterCapabilitiesInfo) * PrinterInfo.Builder.setCapabilities(PrinterCapabilitiesInfo) */ - public abstract void onRequestPrinterUpdate(PrinterId printerId); + public abstract void onStartPrinterStateTracking(PrinterId printerId); + + /** + * Callback asking you to stop tracking the state of a printer. The passed + * in printer id is the one for which you received a call to {@link + * #onStartPrinterStateTracking(PrinterId)}. + * + * @param printerId The printer to stop tracking. + * + * @see #onStartPrinterStateTracking(PrinterId) + */ + public abstract void onStopPrinterStateTracking(PrinterId printerId); /** * Notifies you that the session is destroyed. After this callback is invoked @@ -538,9 +582,21 @@ public abstract class PrinterDiscoverySession { } } - void requestPrinterUpdate(PrinterId printerId) { - if (!mIsDestroyed) { - onRequestPrinterUpdate(printerId); + void validatePrinters(List<PrinterId> printerIds) { + if (!mIsDestroyed && mObserver != null) { + onValidatePrinters(printerIds); + } + } + + void startPrinterStateTracking(PrinterId printerId) { + if (!mIsDestroyed && mObserver != null) { + onStartPrinterStateTracking(printerId); + } + } + + void stopPrinterStateTracking(PrinterId printerId) { + if (!mIsDestroyed && mObserver != null) { + onStopPrinterStateTracking(printerId); } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index faf6e63..b749aa64 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1915,13 +1915,10 @@ android:description="@string/permdesc_bindNfcService" android:protectionLevel="signature" /> - <!-- Allows an application to call APIs that give it access to all print jobs - on the device. Usually an app can access only the print jobts it created. - This permission is not available to third party applications. - @hide --> - <permission android:name="android.permission.ACCESS_ALL_PRINT_JOBS" - android:label="@string/permlab_accessAllPrintJobs" - android:description="@string/permdesc_accessAllPrintJobs" + <!-- Must be required by the PrintSpooler to ensure that only the system can bind to it. --> + <permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE" + android:label="@string/permlab_bindPrintSpoolerService" + android:description="@string/permdesc_bindPrintSpoolerService" android:protectionLevel="signature" /> <!-- Must be required by a TextService (e.g. SpellCheckerService) diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index ead46c2..4b32e2b 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -984,12 +984,13 @@ <string name="permdesc_bindPrintService">Allows the holder to bind to the top-level interface of a print service. Should never be needed for normal apps.</string> - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_accessAllPrintJobs">access all print jobs</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_accessAllPrintJobs">Allows the holder to access print jobs - created by another app. Should never be needed for normal apps.</string> - + <!-- Title of an application permission, listed so the user can choose + whether they want to allow the application to do this. --> + <string name="permlab_bindPrintSpoolerService">bind to a print spooler service</string> + <!-- Description of an application permission, listed so the user can + choose whether they want to allow the application to do this. --> + <string name="permdesc_bindPrintSpoolerService">Allows the holder to bind to the top-level + interface of a print spooler service. Should never be needed for normal apps.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_bindNfcService">bind to NFC service</string> @@ -4292,6 +4293,9 @@ <!-- Write fail reason: couldn't write the printed content. [CHAR LIMIT=none] --> <string name="write_fail_reason_cannot_write">Error writing content</string> + <!-- Print fail reason: unknown. [CHAR LIMIT=25] --> + <string name="reason_unknown">unknown</string> + <!-- PIN entry dialog label/hint for PIN [CHAR LIMIT=none] --> <string name="restr_pin_enter_pin">Enter PIN</string> <!-- PIN entry dialog label/hint for old PIN [CHAR LIMIT=none] --> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index a9c812e..67f25d1 100755 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -868,6 +868,7 @@ <java-symbol type="string" name="mediaSize_na_junior_legal" /> <java-symbol type="string" name="mediaSize_na_ledger" /> <java-symbol type="string" name="mediaSize_na_tabloid" /> + <java-symbol type="string" name="reason_unknown" /> <java-symbol type="string" name="restr_pin_enter_pin" /> <java-symbol type="string" name="write_fail_reason_cancelled" /> <java-symbol type="string" name="write_fail_reason_cannot_write" /> diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk index 8ae0302..f65fe4b 100644 --- a/packages/PrintSpooler/Android.mk +++ b/packages/PrintSpooler/Android.mk @@ -24,8 +24,6 @@ LOCAL_PACKAGE_NAME := PrintSpooler LOCAL_JAVA_LIBRARIES := framework-base -LOCAL_CERTIFICATE := platform - LOCAL_PROGUARD_ENABLED := disabled include $(BUILD_PACKAGE) diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml index ab7ea09..6fa3ed4 100644 --- a/packages/PrintSpooler/AndroidManifest.xml +++ b/packages/PrintSpooler/AndroidManifest.xml @@ -20,18 +20,22 @@ package="com.android.printspooler" android:sharedUserId="android.uid.printspooler" android:versionName="1" - android:versionCode="1" - coreApp="true"> + android:versionCode="1"> - <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="18"/> + <!-- Allows an application to call APIs that give it access to all print jobs + on the device. Usually an app can access only the print jobs it created. + --> + <permission + android:name="com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS" + android:label="@string/permlab_accessAllPrintJobs" + android:description="@string/permdesc_accessAllPrintJobs" + android:protectionLevel="signature" /> + <uses-permission android:name="com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS"/> <uses-permission android:name="android.permission.ACCESS_ALL_PRINT_JOBS"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> - <permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE" - android:label="@string/permlab_bindPrintSpoolerService" - android:description="@string/permdesc_bindPrintSpoolerService" - android:protectionLevel="signature" /> + <uses-sdk android:minSdkVersion="18" android:targetSdkVersion="18"/> <application android:allowClearUserData="false" diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml index ee3cf84..235a7a1 100644 --- a/packages/PrintSpooler/res/values/strings.xml +++ b/packages/PrintSpooler/res/values/strings.xml @@ -94,6 +94,9 @@ <!-- Template for the notificaiton label for a failed print job. [CHAR LIMIT=25] --> <string name="failed_notification_title_template">Printer error <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string> + <!-- Template for the notificaiton label for a blocked print job. [CHAR LIMIT=25] --> + <string name="blocked_notification_title_template">Printer blocked <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string> + <!-- Label for the notification button for cancelling a print job. [CHAR LIMIT=25] --> <string name="cancel">Cancel</string> @@ -103,6 +106,9 @@ <!-- Message that there is no connection to a printer. [CHAR LIMIT=40] --> <string name="no_connection_to_printer">No connection to printer</string> + <!-- Label for an unknown reason for failed or blocked print job. [CHAR LIMIT=25] --> + <string name="reason_unknown">unknown</string> + <!-- Arrays --> <!-- Color mode labels. --> @@ -129,12 +135,14 @@ <item>Range</item> </string-array> - <!-- Title of an application permission, listed so the user can choose - whether they want to allow the application to do this. --> - <string name="permlab_bindPrintSpoolerService">bind to a print spooler service</string> - <!-- Description of an application permission, listed so the user can - choose whether they want to allow the application to do this. --> - <string name="permdesc_bindPrintSpoolerService">Allows the holder to bind to the top-level - interface of a print spooler service. Should never be needed for normal apps.</string> + <!-- Permissions --> + + <!-- Title of an application permission, listed so the user can choose whether they want + to allow the application to do this. --> + <string name="permlab_accessAllPrintJobs">access all print jobs</string> + <!-- Description of an application permission, listed so the user can choose whether + they want to allow the application to do this. --> + <string name="permdesc_accessAllPrintJobs">Allows the holder to access print jobs + created by another app. Should never be needed for normal apps.</string> </resources> diff --git a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java index 28fd0e0..ad8d95a 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java +++ b/packages/PrintSpooler/src/com/android/printspooler/FusedPrintersProvider.java @@ -75,6 +75,8 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { private List<PrinterInfo> mFavoritePrinters; + private PrinterId mTrackedPrinter; + public FusedPrintersProvider(Context context) { super(context); mPersistenceManager = new PersistenceManager(context); @@ -166,6 +168,10 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { private boolean cancelInternal() { if (mDiscoverySession != null && mDiscoverySession.isPrinterDiscoveryStarted()) { + if (mTrackedPrinter != null) { + mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter); + mTrackedPrinter = null; + } mDiscoverySession.stopPrinterDiscovery(); return true; } else if (mPersistenceManager.isReadHistoryInProgress()) { @@ -195,10 +201,14 @@ public class FusedPrintersProvider extends Loader<List<PrinterInfo>> { onStopLoading(); } - public void refreshPrinter(PrinterId printerId) { + public void setTrackedPrinter(PrinterId printerId) { if (isStarted() && mDiscoverySession != null && mDiscoverySession.isPrinterDiscoveryStarted()) { - mDiscoverySession.requestPrinterUpdate(printerId); + if (mTrackedPrinter != null) { + mDiscoverySession.stopPrinterStateTracking(mTrackedPrinter); + } + mTrackedPrinter = printerId; + mDiscoverySession.startPrinterStateTracking(printerId); } } diff --git a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java index c116d37..43a751c 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java +++ b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java @@ -32,6 +32,7 @@ import android.os.UserHandle; import android.print.IPrintManager; import android.print.PrintJobInfo; import android.print.PrintManager; +import android.text.TextUtils; import android.util.Log; /** @@ -64,22 +65,27 @@ public class NotificationController { + " state:" + PrintJobInfo.stateToString(printJob.getState())); } switch (printJob.getState()) { - case PrintJobInfo.STATE_QUEUED: { - createPrintingNotificaiton(printJob); + case PrintJobInfo.STATE_QUEUED: + case PrintJobInfo.STATE_STARTED: { + createPrintingNotification(printJob); } break; case PrintJobInfo.STATE_FAILED: { - createFailedNotificaiton(printJob); + createFailedNotification(printJob); } break; case PrintJobInfo.STATE_COMPLETED: case PrintJobInfo.STATE_CANCELED: { removeNotification(printJob.getId()); } break; + + case PrintJobInfo.STATE_BLOCKED: { + createBlockedNotification(printJob); + } break; } } - private void createPrintingNotificaiton(PrintJobInfo printJob) { + private void createPrintingNotification(PrintJobInfo printJob) { Notification.Builder builder = new Notification.Builder(mContext) .setSmallIcon(R.drawable.stat_notify_print) .setContentTitle(mContext.getString(R.string.printing_notification_title_template, @@ -93,17 +99,36 @@ public class NotificationController { mNotificationManager.notify(printJob.getId(), builder.build()); } - private void createFailedNotificaiton(PrintJobInfo printJob) { + private void createFailedNotification(PrintJobInfo printJob) { + String reason = !TextUtils.isEmpty(printJob.getStateReason()) + ? printJob.getStateReason() : mContext.getString(R.string.reason_unknown); + Notification.Builder builder = new Notification.Builder(mContext) .setSmallIcon(R.drawable.stat_notify_error) .setContentTitle(mContext.getString(R.string.failed_notification_title_template, printJob.getLabel())) .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel), createCancelIntent(printJob)) - // TODO: Use appropriate icon when assets are ready .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.restart), createRestartIntent(printJob.getId())) - .setContentText(printJob.getFailureReason()) + .setContentText(reason) + .setWhen(System.currentTimeMillis()) + .setOngoing(true) + .setShowWhen(true); + mNotificationManager.notify(printJob.getId(), builder.build()); + } + + private void createBlockedNotification(PrintJobInfo printJob) { + String reason = !TextUtils.isEmpty(printJob.getStateReason()) + ? printJob.getStateReason() : mContext.getString(R.string.reason_unknown); + + Notification.Builder builder = new Notification.Builder(mContext) + .setSmallIcon(R.drawable.stat_notify_error) + .setContentTitle(mContext.getString(R.string.blocked_notification_title_template, + printJob.getLabel())) + .addAction(R.drawable.stat_notify_cancelling, mContext.getString(R.string.cancel), + createCancelIntent(printJob)) + .setContentText(reason) .setWhen(System.currentTimeMillis()) .setOngoing(true) .setShowWhen(true); diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java index 607be90..520331c 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java @@ -473,6 +473,11 @@ public class PrintJobConfigActivity extends Activity { mControllerState = CONTROLLER_STATE_WRITE_COMPLETED; + // Update the document size. + File file = PrintSpoolerService.peekInstance() + .generateFileForPrintJob(mPrintJobId); + mDocument.info.setDataSize(file.length()); + // Update which pages we have fetched. mDocument.pages = PageRangeUtils.normalize(pages); @@ -1117,7 +1122,7 @@ public class PrintJobConfigActivity extends Activity { (Loader<?>) getLoaderManager().getLoader( LOADER_ID_PRINTERS_LOADER); if (printersLoader != null) { - printersLoader.refreshPrinter(printer.getId()); + printersLoader.setTrackedPrinter(printer.getId()); } } } @@ -1351,10 +1356,6 @@ public class PrintJobConfigActivity extends Activity { return mEditorState == EDITOR_STATE_CONFIRMED_PRINT; } -// public void confirmPreview() { -// mEditorState = EDITOR_STATE_CONFIRMED_PREVIEW; -// } - public PageRange[] getRequestedPages() { if (hasErrors()) { return null; @@ -1374,7 +1375,7 @@ public class PrintJobConfigActivity extends Activity { toIndex = Integer.parseInt(range.substring( dashIndex + 1, range.length())) - 1; } else { - fromIndex = toIndex = Integer.parseInt(range); + fromIndex = toIndex = Integer.parseInt(range) - 1; } PageRange pageRange = new PageRange(fromIndex, toIndex); diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java index c1f4180..dd2598c 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java +++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java @@ -335,7 +335,9 @@ public final class PrintSpoolerService extends Service { final boolean sameState = (state == printJob.getState()) || (state == PrintJobInfo.STATE_ANY) || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS - && printJob.getState() > PrintJobInfo.STATE_CREATED); + && isStateVisibleToUser(printJob.getState())) + || (state == PrintJobInfo.STATE_ANY_ACTIVE + && isActiveState(printJob.getState())); if (sameComponent && sameAppId && sameState) { if (foundPrintJobs == null) { foundPrintJobs = new ArrayList<PrintJobInfo>(); @@ -347,6 +349,11 @@ public final class PrintSpoolerService extends Service { return foundPrintJobs; } + private boolean isStateVisibleToUser(int state) { + return (isActiveState(state) && (state == PrintJobInfo.STATE_FAILED + || state == PrintJobInfo.STATE_COMPLETED|| state == PrintJobInfo.STATE_CANCELED)); + } + public PrintJobInfo getPrintJobInfo(int printJobId, int appId) { synchronized (mLock) { final int printJobCount = mPrintJobs.size(); @@ -389,14 +396,12 @@ public final class PrintSpoolerService extends Service { switch (printJob.getState()) { case PrintJobInfo.STATE_QUEUED: - case PrintJobInfo.STATE_STARTED: { - // We have a print job that was queued or started in the - // past - // but the device battery died or a crash occurred. In this - // case - // we assume the print job failed and let the user decide - // whether - // to restart the job or just + case PrintJobInfo.STATE_STARTED: + case PrintJobInfo.STATE_BLOCKED: { + // We have a print job that was queued or started or blocked in + // the past but the device battery died or a crash occurred. In + // this case we assume the print job failed and let the user + // decide whether to restart the job or just cancel it. setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED, getString(R.string.no_connection_to_printer)); } @@ -501,7 +506,7 @@ public final class PrintSpoolerService extends Service { success = true; printJob.setState(state); - printJob.setFailureReason(error); + printJob.setStateReason(error); mNotificationController.onPrintJobStateChanged(printJob); if (DEBUG_PRINT_JOB_LIFECYCLE) { @@ -568,7 +573,8 @@ public final class PrintSpoolerService extends Service { private boolean isActiveState(int printJobState) { return printJobState == PrintJobInfo.STATE_CREATED || printJobState == PrintJobInfo.STATE_QUEUED - || printJobState == PrintJobInfo.STATE_STARTED; + || printJobState == PrintJobInfo.STATE_STARTED + || printJobState == PrintJobInfo.STATE_BLOCKED; } public boolean setPrintJobTag(int printJobId, String tag) { diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java index 671a5dc..2563b58 100644 --- a/services/java/com/android/server/print/PrintManagerService.java +++ b/services/java/com/android/server/print/PrintManagerService.java @@ -254,7 +254,7 @@ public final class PrintManagerService extends IPrintManager.Stub { } @Override - public void requestPrinterUpdate(PrinterId printerId, int userId) { + public void validatePrinters(List<PrinterId> printerIds, int userId) { final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); final UserState userState; synchronized (mLock) { @@ -262,7 +262,37 @@ public final class PrintManagerService extends IPrintManager.Stub { } final long identity = Binder.clearCallingIdentity(); try { - userState.requestPrinterUpdate(printerId); + userState.validatePrinters(printerIds); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void startPrinterStateTracking(PrinterId printerId, int userId) { + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(resolvedUserId); + } + final long identity = Binder.clearCallingIdentity(); + try { + userState.startPrinterStateTracking(printerId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override + public void stopPrinterStateTracking(PrinterId printerId, int userId) { + final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId); + final UserState userState; + synchronized (mLock) { + userState = getOrCreateUserStateLocked(resolvedUserId); + } + final long identity = Binder.clearCallingIdentity(); + try { + userState.stopPrinterStateTracking(printerId); } finally { Binder.restoreCallingIdentity(identity); } @@ -432,10 +462,12 @@ public final class PrintManagerService extends IPrintManager.Stub { if (appId == callingAppId) { return appId; } - if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS) + if (mContext.checkCallingPermission( + "com.android.printspooler.permission.ACCESS_ALL_PRINT_JOBS") != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Call from app " + callingAppId + " as app " - + appId + " without permission ACCESS_ALL_PRINT_JOBS"); + + appId + " without com.android.printspooler.permission" + + ".ACCESS_ALL_PRINT_JOBS"); } return appId; } diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java index 7f4b343..14af9d8 100644 --- a/services/java/com/android/server/print/RemotePrintService.java +++ b/services/java/com/android/server/print/RemotePrintService.java @@ -25,6 +25,7 @@ import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.IBinder.DeathRecipient; +import android.os.AsyncTask; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; @@ -38,6 +39,8 @@ import android.printservice.IPrintService; import android.printservice.IPrintServiceClient; import android.util.Slog; +import com.android.internal.R; + import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; @@ -121,6 +124,36 @@ final class RemotePrintService implements DeathRecipient { mHasPrinterDiscoverySession = false; mPendingCommands.clear(); ensureUnbound(); + + // Makes sure all active print jobs are failed since the service + // just died. Do this off the main thread since we do to allow + // calls into the spooler on the main thread. + new AsyncTask<Void, Void, Void>() { + @Override + protected Void doInBackground(Void... params) { + failAllActivePrintJobs(); + return null; + } + }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null); + } + + private void failAllActivePrintJobs() { + List<PrintJobInfo> printJobs = mSpooler.getPrintJobInfos(mComponentName, + PrintJobInfo.STATE_ANY_ACTIVE, PrintManager.APP_ID_ANY); + if (printJobs == null) { + return; + } + final long identity = Binder.clearCallingIdentity(); + try { + final int printJobCount = printJobs.size(); + for (int i = 0; i < printJobCount; i++) { + PrintJobInfo printJob = printJobs.get(i); + mSpooler.setPrintJobState(printJob.getId(), PrintJobInfo.STATE_FAILED, + mContext.getString(R.string.reason_unknown)); + } + } finally { + Binder.restoreCallingIdentity(identity); + } } private void handleOnAllPrintJobsHandled() { @@ -308,29 +341,83 @@ final class RemotePrintService implements DeathRecipient { } } - public void requestPrinterUpdate(PrinterId printerId) { - mHandler.obtainMessage(MyHandler.MSG_REQUEST_PRINTER_UPDATE, + public void validatePrinters(List<PrinterId> printerIds) { + mHandler.obtainMessage(MyHandler.MSG_VALIDATE_PRINTERS, + printerIds).sendToTarget(); + } + + private void handleValidatePrinters(final List<PrinterId> printerIds) { + throwIfDestroyed(); + if (!isBound()) { + ensureBound(); + mPendingCommands.add(new Runnable() { + @Override + public void run() { + handleValidatePrinters(printerIds); + } + }); + } else { + if (DEBUG) { + Slog.i(LOG_TAG, "[user: " + mUserId + "] handleValidatePrinters()"); + } + try { + mPrintService.validatePrinters(printerIds); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Error requesting printers validation.", re); + } + } + } + + public void startPrinterStateTracking(PrinterId printerId) { + mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_STATE_TRACKING, + printerId).sendToTarget(); + } + + private void handleStartPrinterStateTracking(final PrinterId printerId) { + throwIfDestroyed(); + if (!isBound()) { + ensureBound(); + mPendingCommands.add(new Runnable() { + @Override + public void run() { + handleStartPrinterStateTracking(printerId); + } + }); + } else { + if (DEBUG) { + Slog.i(LOG_TAG, "[user: " + mUserId + "] handleStartPrinterTracking()"); + } + try { + mPrintService.startPrinterStateTracking(printerId); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Error requesting start printer tracking.", re); + } + } + } + + public void stopPrinterStateTracking(PrinterId printerId) { + mHandler.obtainMessage(MyHandler.MSG_STOP_PRINTER_STATE_TRACKING, printerId).sendToTarget(); } - private void handleRequestPrinterUpdate(final PrinterId printerId) { + private void handleStopPrinterStateTracking(final PrinterId printerId) { throwIfDestroyed(); if (!isBound()) { ensureBound(); mPendingCommands.add(new Runnable() { @Override public void run() { - handleRequestPrinterUpdate(printerId); + handleStopPrinterStateTracking(printerId); } }); } else { if (DEBUG) { - Slog.i(LOG_TAG, "[user: " + mUserId + "] requestPrinterUpdate()"); + Slog.i(LOG_TAG, "[user: " + mUserId + "] handleStopPrinterTracking()"); } try { - mPrintService.requestPrinterUpdate(printerId); + mPrintService.stopPrinterStateTracking(printerId); } catch (RemoteException re) { - Slog.e(LOG_TAG, "Error requesting a printer update.", re); + Slog.e(LOG_TAG, "Error requesting stop printer tracking.", re); } } } @@ -417,12 +504,14 @@ final class RemotePrintService implements DeathRecipient { public static final int MSG_DESTROY_PRINTER_DISCOVERY_SESSION = 2; public static final int MSG_START_PRINTER_DISCOVERY = 3; public static final int MSG_STOP_PRINTER_DISCOVERY = 4; - public static final int MSG_REQUEST_PRINTER_UPDATE = 5; - public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 6; - public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 7; - public static final int MSG_ON_PRINT_JOB_QUEUED = 8; - public static final int MSG_DESTROY = 9; - public static final int MSG_BINDER_DIED = 10; + public static final int MSG_VALIDATE_PRINTERS = 5; + public static final int MSG_START_PRINTER_STATE_TRACKING = 6; + public static final int MSG_STOP_PRINTER_STATE_TRACKING = 7; + public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 8; + public static final int MSG_ON_REQUEST_CANCEL_PRINT_JOB = 9; + public static final int MSG_ON_PRINT_JOB_QUEUED = 10; + public static final int MSG_DESTROY = 11; + public static final int MSG_BINDER_DIED = 12; public MyHandler(Looper looper) { super(looper, null, false); @@ -449,9 +538,19 @@ final class RemotePrintService implements DeathRecipient { handleStopPrinterDiscovery(); } break; - case MSG_REQUEST_PRINTER_UPDATE: { + case MSG_VALIDATE_PRINTERS: { + List<PrinterId> printerIds = (List<PrinterId>) message.obj; + handleValidatePrinters(printerIds); + } break; + + case MSG_START_PRINTER_STATE_TRACKING: { + PrinterId printerId = (PrinterId) message.obj; + handleStartPrinterStateTracking(printerId); + } break; + + case MSG_STOP_PRINTER_STATE_TRACKING: { PrinterId printerId = (PrinterId) message.obj; - handleRequestPrinterUpdate(printerId); + handleStopPrinterStateTracking(printerId); } break; case MSG_ON_ALL_PRINT_JOBS_HANDLED: { diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java index c979a11..4a1b96b 100644 --- a/services/java/com/android/server/print/UserState.java +++ b/services/java/com/android/server/print/UserState.java @@ -19,8 +19,12 @@ package com.android.server.print; import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; import android.os.Build; import android.os.Handler; import android.os.IBinder; @@ -46,6 +50,7 @@ import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; @@ -63,6 +68,11 @@ final class UserState implements PrintSpoolerCallbacks { private static final char COMPONENT_NAME_SEPARATOR = ':'; + private static final String SHARED_PREFERENCES_FILE = "shared_prefs"; + + private static final String KEY_SYSTEM_PRINT_SERVICES_ENABLED = + "KEY_SYSTEM_PRINT_SERVICES_ENABLED"; + private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR); @@ -95,6 +105,7 @@ final class UserState implements PrintSpoolerCallbacks { mUserId = userId; mLock = lock; mSpooler = new RemotePrintSpooler(context, userId, this); + enableSystemPrintServicesOnce(); } @Override @@ -190,7 +201,7 @@ final class UserState implements PrintSpoolerCallbacks { } } - public void requestPrinterUpdate(PrinterId printerId) { + public void validatePrinters(List<PrinterId> printerIds) { synchronized (mLock) { throwIfDestroyedLocked(); // No services - nothing to do. @@ -202,7 +213,39 @@ final class UserState implements PrintSpoolerCallbacks { return; } // Request an updated. - mPrinterDiscoverySession.requestPrinterUpdateLocked(printerId); + mPrinterDiscoverySession.validatePrintersLocked(printerIds); + } + } + + public void startPrinterStateTracking(PrinterId printerId) { + synchronized (mLock) { + throwIfDestroyedLocked(); + // No services - nothing to do. + if (mActiveServices.isEmpty()) { + return; + } + // No session - nothing to do. + if (mPrinterDiscoverySession == null) { + return; + } + // Request start tracking the printer. + mPrinterDiscoverySession.startPrinterStateTrackingLocked(printerId); + } + } + + public void stopPrinterStateTracking(PrinterId printerId) { + synchronized (mLock) { + throwIfDestroyedLocked(); + // No services - nothing to do. + if (mActiveServices.isEmpty()) { + return; + } + // No session - nothing to do. + if (mPrinterDiscoverySession == null) { + return; + } + // Request stop tracking the printer. + mPrinterDiscoverySession.stopPrinterStateTrackingLocked(printerId); } } @@ -365,6 +408,36 @@ final class UserState implements PrintSpoolerCallbacks { return false; } + private void enableSystemPrintServicesOnce() { + SharedPreferences preferences = mContext.getSharedPreferences( + SHARED_PREFERENCES_FILE, Context.MODE_PRIVATE); + if (preferences.getInt(KEY_SYSTEM_PRINT_SERVICES_ENABLED, 0) == 0) { + Editor editor = preferences.edit(); + editor.putInt(KEY_SYSTEM_PRINT_SERVICES_ENABLED, 1); + editor.commit(); + + readInstalledPrintServicesLocked(); + + StringBuilder builder = new StringBuilder(); + + final int serviceCount = mInstalledServices.size(); + for (int i = 0; i < serviceCount; i++) { + ServiceInfo serviceInfo = mInstalledServices.get(i).getResolveInfo().serviceInfo; + if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + ComponentName serviceName = new ComponentName( + serviceInfo.packageName, serviceInfo.name); + if (builder.length() > 0) { + builder.append(":"); + } + builder.append(serviceName.flattenToString()); + } + } + + Settings.Secure.putStringForUser(mContext.getContentResolver(), + Settings.Secure.ENABLED_PRINT_SERVICES, builder.toString(), mUserId); + } + } + private void onConfigurationChangedLocked() { final int installedCount = mInstalledServices.size(); for (int i = 0; i < installedCount; i++) { @@ -415,6 +488,8 @@ final class UserState implements PrintSpoolerCallbacks { private final List<IBinder> mStartedPrinterDiscoveryTokens = new ArrayList<IBinder>(); + private final List<PrinterId> mStateTrackedPrinters = new ArrayList<PrinterId>(); + private final Handler mHandler; private boolean mIsDestroyed; @@ -461,14 +536,10 @@ final class UserState implements PrintSpoolerCallbacks { } // If printer discovery is ongoing and the start request has a list - // of printer to be checked, then we just request refreshing each of - // them rather making another start discovery request. + // of printer to be checked, then we just request validating them. if (!mStartedPrinterDiscoveryTokens.isEmpty() && priorityList != null && !priorityList.isEmpty()) { - final int priorityIdCount = priorityList.size(); - for (int i = 0; i < priorityIdCount; i++) { - requestPrinterUpdate(priorityList.get(i)); - } + validatePrinters(priorityList); return; } @@ -508,20 +579,97 @@ final class UserState implements PrintSpoolerCallbacks { .sendToTarget(); } - public void requestPrinterUpdateLocked(PrinterId printerId) { + public void validatePrintersLocked(List<PrinterId> printerIds) { if (mIsDestroyed) { - Log.w(LOG_TAG, "Not updating pritner - session destroyed"); + Log.w(LOG_TAG, "Not validating pritners - session destroyed"); return; } + + List<PrinterId> remainingList = new ArrayList<PrinterId>(printerIds); + while (!remainingList.isEmpty()) { + Iterator<PrinterId> iterator = remainingList.iterator(); + // Gather the printers per service and request a validation. + List<PrinterId> updateList = new ArrayList<PrinterId>(); + ComponentName serviceName = null; + while (iterator.hasNext()) { + PrinterId printerId = iterator.next(); + if (updateList.isEmpty()) { + updateList.add(printerId); + serviceName = printerId.getServiceName(); + iterator.remove(); + } else if (printerId.getServiceName().equals(serviceName)) { + updateList.add(printerId); + iterator.remove(); + } + } + // Schedule a notification of the service. + RemotePrintService service = mActiveServices.get(serviceName); + if (service != null) { + SomeArgs args = SomeArgs.obtain(); + args.arg1 = service; + args.arg2 = updateList; + mHandler.obtainMessage(SessionHandler + .MSG_VALIDATE_PRINTERS, args) + .sendToTarget(); + } + } + } + + public final void startPrinterStateTrackingLocked(PrinterId printerId) { + if (mIsDestroyed) { + Log.w(LOG_TAG, "Not starting printer state tracking - session destroyed"); + return; + } + // If printer discovery is not started - nothing to do. + if (mStartedPrinterDiscoveryTokens.isEmpty()) { + return; + } + final boolean containedPrinterId = mStateTrackedPrinters.contains(printerId); + // Keep track of the number of requests to track this one. + mStateTrackedPrinters.add(printerId); + // If we were tracking this printer - nothing to do. + if (containedPrinterId) { + return; + } + // No service - nothing to do. RemotePrintService service = mActiveServices.get(printerId.getServiceName()); - if (service != null) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = service; - args.arg2 = printerId; - mHandler.obtainMessage(SessionHandler - .MSG_REQUEST_PRINTER_UPDATE, args) - .sendToTarget(); + if (service == null) { + return; } + // Ask the service to start tracking. + SomeArgs args = SomeArgs.obtain(); + args.arg1 = service; + args.arg2 = printerId; + mHandler.obtainMessage(SessionHandler + .MSG_START_PRINTER_STATE_TRACKING, args) + .sendToTarget(); + } + + public final void stopPrinterStateTrackingLocked(PrinterId printerId) { + if (mIsDestroyed) { + Log.w(LOG_TAG, "Not stopping printer state tracking - session destroyed"); + return; + } + // If printer discovery is not started - nothing to do. + if (mStartedPrinterDiscoveryTokens.isEmpty()) { + return; + } + // If we did not track this printer - nothing to do. + if (!mStateTrackedPrinters.remove(printerId)) { + return; + } + // No service - nothing to do. + RemotePrintService service = mActiveServices.get(printerId.getServiceName()); + if (service == null) { + return; + } + // Ask the service to start tracking. + SomeArgs args = SomeArgs.obtain(); + args.arg1 = service; + args.arg2 = printerId; + mHandler.obtainMessage(SessionHandler + .MSG_STOP_PRINTER_STATE_TRACKING, args) + .sendToTarget(); } public void onDestroyed() { @@ -533,6 +681,12 @@ final class UserState implements PrintSpoolerCallbacks { Log.w(LOG_TAG, "Not destroying - session destroyed"); return; } + // Make sure printer tracking is stopped. + final int printerCount = mStateTrackedPrinters.size(); + for (int i = 0; i < printerCount; i++) { + PrinterId printerId = mStateTrackedPrinters.get(i); + stopPrinterStateTracking(printerId); + } // Make sure discovery is stopped. final int observerCount = mStartedPrinterDiscoveryTokens.size(); for (int i = 0; i < observerCount; i++) { @@ -744,9 +898,19 @@ final class UserState implements PrintSpoolerCallbacks { } } - private void handleRequestPrinterUpdate(RemotePrintService service, + private void handleValidatePrinters(RemotePrintService service, + List<PrinterId> printerIds) { + service.validatePrinters(printerIds); + } + + private void handleStartPrinterStateTracking(RemotePrintService service, + PrinterId printerId) { + service.startPrinterStateTracking(printerId); + } + + private void handleStopPrinterStateTracking(RemotePrintService service, PrinterId printerId) { - service.requestPrinterUpdate(printerId); + service.stopPrinterStateTracking(printerId); } private void handlePrintersAdded(IPrinterDiscoveryObserver observer, @@ -804,7 +968,9 @@ final class UserState implements PrintSpoolerCallbacks { public static final int MSG_DISPATCH_DESTROY_PRINTER_DISCOVERY_SESSION = 9; public static final int MSG_DISPATCH_START_PRINTER_DISCOVERY = 10; public static final int MSG_DISPATCH_STOP_PRINTER_DISCOVERY = 11; - public static final int MSG_REQUEST_PRINTER_UPDATE = 12; + public static final int MSG_VALIDATE_PRINTERS = 12; + public static final int MSG_START_PRINTER_STATE_TRACKING = 13; + public static final int MSG_STOP_PRINTER_STATE_TRACKING = 14; SessionHandler(Looper looper) { super(looper, null, false); @@ -878,13 +1044,29 @@ final class UserState implements PrintSpoolerCallbacks { handleDispatchStopPrinterDiscovery(services); } break; - case MSG_REQUEST_PRINTER_UPDATE: { + case MSG_VALIDATE_PRINTERS: { + SomeArgs args = (SomeArgs) message.obj; + RemotePrintService service = (RemotePrintService) args.arg1; + List<PrinterId> printerIds = (List<PrinterId>) args.arg2; + args.recycle(); + handleValidatePrinters(service, printerIds); + } break; + + case MSG_START_PRINTER_STATE_TRACKING: { SomeArgs args = (SomeArgs) message.obj; RemotePrintService service = (RemotePrintService) args.arg1; PrinterId printerId = (PrinterId) args.arg2; args.recycle(); - handleRequestPrinterUpdate(service, printerId); + handleStartPrinterStateTracking(service, printerId); } break; + + case MSG_STOP_PRINTER_STATE_TRACKING: { + SomeArgs args = (SomeArgs) message.obj; + RemotePrintService service = (RemotePrintService) args.arg1; + PrinterId printerId = (PrinterId) args.arg2; + args.recycle(); + handleStopPrinterStateTracking(service, printerId); + } } } } |