Print spooler security and some new print service facing APIs.

1. Updated the security mode of the print spooler. Now the spooler
   is not signed with the system key, it is not a privileged app so if
   it gets compromised (PDF rendering is a potential attack vector)
   it cannot access dangerous permissions. Also only the system
   can bind to the spooler.

2. Added APIs for asking a print service to start and stop tracking
   a given printer. This is need for the case when the user selects
   the printer and the print service should do a best effort to keep
   the system updated for the current state of the printer.

3. Added APIs for putting a print job in a blocked state. A print
   service would report the print job as blocked if for some reason
   the printer cannot proceed, e.g. 99 pages are printed but there
   is no paper for the last one. The user has to add more paper
   and the print service can resume the job.

4. Changed the read/write APIs to use ParcelFileDescriptor instead
   of FileDescriptor since the latter does not have a clean API for
   detaching the wrapped Linux file descriptor when one wants to
   push it to native.

5. Added API for getting the size of the printed document so the
   print service can avoid handling big filed over cellular network
   or ask the user if needed.

6. Now the print services that are preinstalled on the system image
   are automatically enabled.

Change-Id: Ia06c311d3d21cabb9e1368f13928e11cd0030918
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 @@
     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 @@
 
 import android.os.Bundle;
 import android.os.CancellationSignal;
+import android.os.ParcelFileDescriptor;
 
-import java.io.FileDescriptor;
 import java.util.List;
 
 /**
@@ -41,7 +41,7 @@
  * <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 @@
  * 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 @@
      * 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 @@
      * @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 @@
 
     /**
      * 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 @@
     private int mColorMode;
     private Margins mMargins;
     private MediaSize mMediaSize;
+    private long mDataSize;
 
     /**
      * Creates a new instance.
@@ -82,6 +83,7 @@
         mColorMode = prototype.mColorMode;
         mMargins = prototype.mMargins;
         mMediaSize = prototype.mMediaSize;
+        mDataSize = prototype.mDataSize;
     }
 
     /**
@@ -98,6 +100,7 @@
         mColorMode = parcel.readInt();
         mMargins = Margins.createFromParcel(parcel);
         mMediaSize = MediaSize.createFromParcel(parcel);
+        mDataSize = parcel.readLong();
     }
 
     /**
@@ -188,6 +191,26 @@
         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 @@
         parcel.writeInt(mColorMode);
         mMargins.writeToParcel(parcel);
         mMediaSize.writeToParcel(parcel);
+        parcel.writeLong(mDataSize);
     }
 
     @Override
@@ -217,6 +241,8 @@
         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 @@
         } else if (!mMediaSize.equals(other.mMediaSize)) {
             return false;
         }
+        if (mDataSize != other.mDataSize) {
+            return false;
+        }
         return true;
     }
 
@@ -279,6 +308,7 @@
         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.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 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 @@
     }
 
     @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 @@
 
     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 @@
         @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 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 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 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.
-     * This is a terminal state.
+     * Print job state: The print job is blocked.
      * <p>
-     * Next valid states: None
+     * Next valid states: {@link #STATE_FAILED}, {@link #STATE_CANCELED},
+     * {@link #STATE_STARTED}
      * </p>
      */
-    public static final int STATE_COMPLETED = 4;
+    public static final int STATE_BLOCKED = 4;
 
     /**
-     * Print job status: The print job was printing but printing failed.
+     * 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_FAILED = 5;
+    public static final int STATE_COMPLETED = 5;
 
     /**
-     * Print job status: The print job was canceled.
+     * 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_CANCELED = 6;
+    public static final int STATE_FAILED = 6;
+
+    /**
+     * 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 = 7;
 
     /** The unique print job id. */
     private int mId;
@@ -127,8 +143,8 @@
     /** 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 @@
         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 @@
         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 @@
     }
 
     /**
-     * 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 @@
         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 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 @@
      * @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 @@
      * @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 @@
             }
             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 @@
                     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 @@
         }
 
         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 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 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 void requestPrinterUpdate(PrinterId printerId) {
+    public final void startPrinterStateTracking(PrinterId printerId) {
         if (isDestroyed()) {
-            Log.w(LOG_TAG, "Ignoring reqeust printer update - session destroyed");
+            Log.w(LOG_TAG, "Ignoring start printer state tracking - session destroyed");
+            return;
         }
         try {
-            mPrintManager.requestPrinterUpdate(printerId, mUserId);
+            mPrintManager.startPrinterStateTracking(printerId, mUserId);
         } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error requesting printer update", re);
+            Log.e(LOG_TAG, "Error starting printer state tracking", re);
+        }
+    }
+
+    public final void stopPrinterStateTracking(PrinterId printerId) {
+        if (isDestroyed()) {
+            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.validatePrinters(printerIds, mUserId);
+        } catch (RemoteException 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 @@
      * @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 @@
     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..8292cfbc 100644
--- a/core/java/android/printservice/PrintDocument.java
+++ b/core/java/android/printservice/PrintDocument.java
@@ -21,12 +21,15 @@
 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 @@
      * @return The document info.
      */
     public PrintDocumentInfo getInfo() {
+        PrintService.throwIfNotCalledOnMainThread();
         return mInfo;
     }
 
@@ -64,7 +68,8 @@
      *
      * @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 @@
             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 @@
 
 import android.os.RemoteException;
 import android.print.PrintJobInfo;
+import android.text.TextUtils;
 import android.util.Log;
 
 /**
@@ -123,6 +124,21 @@
     }
 
     /**
+     * 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 @@
 
     /**
      * 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 @@
 
     /**
      * 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 @@
      *
      * @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 @@
 
     /**
      * 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 @@
     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 @@
                 // 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 @@
             }
 
             @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 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 @@
                     }
                 } 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.requestPrinterUpdate(printerId);
+                        mDiscoverySession.startPrinterStateTracking(printerId);
+                    }
+                } break;
+
+                case MSG_STOP_PRINTER_STATE_TRACKING: {
+                    if (mDiscoverySession != null) {
+                        PrinterId printerId = (PrinterId) message.obj;
+                        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 @@
  * 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 @@
      * 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 @@
      * 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 @@
      * 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 @@
      * 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 @@
      * <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 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 @@
      * 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 @@
         }
     }
 
-    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..b749aa6 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" />