Merge "Revert "ssss""
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl
index 7155096..37ae2ca 100644
--- a/core/java/android/print/IPrintManager.aidl
+++ b/core/java/android/print/IPrintManager.aidl
@@ -33,5 +33,4 @@
             in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes,
             int appId, int userId);
     void cancelPrintJob(int printJobId, int appId, int userId);
-    void restartPrintJob(int printJobId, int appId, int userId);
 }
diff --git a/core/java/android/print/IPrintSpooler.aidl b/core/java/android/print/IPrintSpooler.aidl
index 428f972..c55205d 100644
--- a/core/java/android/print/IPrintSpooler.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -40,8 +40,10 @@
     void createPrintJob(String printJobName, in IPrintClient client,
             in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes,
             IPrintSpoolerCallbacks callback, int appId, int sequence);
-    void setPrintJobState(int printJobId, int status, CharSequence error,
-            IPrintSpoolerCallbacks callback, int sequence);
+    void cancelPrintJob(int printJobId, IPrintSpoolerCallbacks callback,
+            int appId, int sequence);
+    void setPrintJobState(int printJobId, int status, IPrintSpoolerCallbacks callback,
+            int sequence);
     void setPrintJobTag(int printJobId, String tag, IPrintSpoolerCallbacks callback,
             int sequence);
     void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 096dcd5..39546f3 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -124,9 +124,6 @@
     /** How many copies to print. */
     private int mCopies;
 
-    /** Failure reason if this job failed. */
-    private CharSequence mFailureReason;
-
     /** The pages to print */
     private PageRange[] mPageRanges;
 
@@ -151,7 +148,6 @@
         mUserId = other.mUserId;
         mTag = other.mTag;
         mCopies = other.mCopies;
-        mFailureReason = other.mFailureReason;
         mPageRanges = other.mPageRanges;
         mAttributes = other.mAttributes;
         mDocumentInfo = other.mDocumentInfo;
@@ -167,9 +163,6 @@
         mTag = parcel.readString();
         mCopies = parcel.readInt();
         if (parcel.readInt() == 1) {
-            mFailureReason = parcel.readCharSequence();
-        }
-        if (parcel.readInt() == 1) {
             Parcelable[] parcelables = parcel.readParcelableArray(null);
             mPageRanges = new PageRange[parcelables.length];
             for (int i = 0; i < parcelables.length; i++) {
@@ -352,28 +345,6 @@
     }
 
     /**
-     * The failure reason if this print job failed.
-     *
-     * @return The failure reason.
-     *
-     * @hide
-     */
-    public CharSequence getFailureReason() {
-        return mFailureReason;
-    }
-
-    /**
-     * The failure reason if this print job failed.
-     *
-     * @param failureReason The failure reason.
-     *
-     * @hide
-     */
-    public void setFailureReason(CharSequence failureReason) {
-        mFailureReason = failureReason;
-    }
-
-    /**
      * Gets the included pages.
      *
      * @return The included pages or <code>null</code> if not set.
@@ -450,12 +421,6 @@
         parcel.writeInt(mUserId);
         parcel.writeString(mTag);
         parcel.writeInt(mCopies);
-        if (mFailureReason != null) {
-            parcel.writeInt(1);
-            parcel.writeCharSequence(mFailureReason);
-        } else {
-            parcel.writeInt(0);
-        }
         if (mPageRanges != null) {
             parcel.writeInt(1);
             parcel.writeParcelableArray(mPageRanges, flags);
diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java
index 92f686d..e27fbb2 100644
--- a/core/java/android/print/PrinterId.java
+++ b/core/java/android/print/PrinterId.java
@@ -26,26 +26,26 @@
  */
 public final class PrinterId implements Parcelable {
 
-    private final ComponentName mServiceName;
+    private final ComponentName mServiceComponentName;
 
-    private final String mPrinterName;
+    private final String mLocalId;
 
     /**
      * Creates a new instance.
      *
-     * @param serviceName The managing print service.
-     * @param printerName The unique id within the managing service.
+     * @param serviceComponentName The managing print service.
+     * @param localId The unique id within the managing service.
      *
      * @hide
      */
-    public PrinterId(ComponentName serviceName, String printerName) {
-        mServiceName = serviceName;
-        mPrinterName = printerName;
+    public PrinterId(ComponentName serviceComponentName, String localId) {
+        mServiceComponentName = serviceComponentName;
+        mLocalId = localId;
     }
 
     private PrinterId(Parcel parcel) {
-        mServiceName = parcel.readParcelable(null);
-        mPrinterName = parcel.readString();
+        mServiceComponentName = parcel.readParcelable(null);
+        mLocalId = parcel.readString();
     }
 
     /**
@@ -55,18 +55,18 @@
      *
      * @hide
      */
-    public ComponentName getServiceName() {
-        return mServiceName;
+    public ComponentName getService() {
+        return mServiceComponentName;
     }
 
     /**
-     * Gets the name of this printer which is unique in the context
+     * Gets the local id of this printer in the context
      * of the print service that manages it.
      *
-     * @return The printer name.
+     * @return The local id.
      */
-    public String getPrinterName() {
-        return mPrinterName;
+    public String getLocalId() {
+        return mLocalId;
     }
 
     @Override
@@ -76,8 +76,8 @@
 
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeParcelable(mServiceName, flags);
-        parcel.writeString(mPrinterName);
+        parcel.writeParcelable(mServiceComponentName, flags);
+        parcel.writeString(mLocalId);
     }
 
     @Override
@@ -92,14 +92,14 @@
             return false;
         }
         PrinterId other = (PrinterId) object;
-        if (mServiceName == null) {
-            if (other.mServiceName != null) {
+        if (mServiceComponentName == null) {
+            if (other.mServiceComponentName != null) {
                 return false;
             }
-        } else if (!mServiceName.equals(other.mServiceName)) {
+        } else if (!mServiceComponentName.equals(other.mServiceComponentName)) {
             return false;
         }
-        if (!TextUtils.equals(mPrinterName, other.mPrinterName)) {
+        if (!TextUtils.equals(mLocalId, other.mLocalId)) {
             return false;
         }
         return true;
@@ -109,9 +109,9 @@
     public int hashCode() {
         final int prime = 31;
         int hashCode = 1;
-        hashCode = prime * hashCode + ((mServiceName != null)
-                ? mServiceName.hashCode() : 1);
-        hashCode = prime * hashCode + mPrinterName.hashCode();
+        hashCode = prime * hashCode + ((mServiceComponentName != null)
+                ? mServiceComponentName.hashCode() : 1);
+        hashCode = prime * hashCode + mLocalId.hashCode();
         return hashCode;
     }
 
@@ -119,9 +119,9 @@
     public String toString() {
         StringBuilder builder = new StringBuilder();
         builder.append("PrinterId{");
-        builder.append(mServiceName.flattenToString());
+        builder.append(mServiceComponentName.flattenToString());
         builder.append(":");
-        builder.append(mPrinterName);
+        builder.append(mLocalId);
         builder.append('}');
         return builder.toString();
     }
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 10cecca..15bcb73 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -22,6 +22,7 @@
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Resolution;
 import android.print.PrintAttributes.Tray;
+import android.text.TextUtils;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -58,6 +59,7 @@
     // TODO: Add printer status constants.
 
     private PrinterId mId;
+    private CharSequence mLabel;
     private int mStatus;
 
     private Margins mMinMargins = DEFAULT_MARGINS;
@@ -90,6 +92,7 @@
      */
     public void copyFrom(PrinterInfo other) {
         mId = other.mId;
+        mLabel = other.mLabel;
         mStatus = other.mStatus;
 
         mMinMargins = other.mMinMargins;
@@ -159,6 +162,15 @@
     }
 
     /**
+     * Gets the human readable printer label.
+     *
+     * @return The human readable label.
+     */
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+
+    /**
      * Gets the status of the printer.
      *
      * @return The status.
@@ -331,6 +343,7 @@
 
     private PrinterInfo(Parcel parcel) {
         mId = parcel.readParcelable(null);
+        mLabel = parcel.readCharSequence();
         mStatus = parcel.readInt();
 
         mMinMargins = readMargins(parcel);
@@ -356,6 +369,7 @@
     @Override
     public void writeToParcel(Parcel parcel, int flags) {
         parcel.writeParcelable(mId, flags);
+        parcel.writeCharSequence(mLabel);
         parcel.writeInt(mStatus);
 
         writeMargins(mMinMargins, parcel);
@@ -378,6 +392,7 @@
         final int prime = 31;
         int result = 1;
         result = prime * result + ((mId == null) ? 0 : mId.hashCode());
+        result = prime * result + ((mLabel == null) ? 0 : mLabel.hashCode());
         result = prime * result + mStatus;
         result = prime * result + ((mMinMargins == null) ? 0 : mMinMargins.hashCode());
         result = prime * result + ((mMediaSizes == null) ? 0 : mMediaSizes.hashCode());
@@ -412,6 +427,9 @@
         } else if (!mId.equals(other.mId)) {
             return false;
         }
+        if (!TextUtils.equals(mLabel, other.mLabel)) {
+            return false;
+        }
         if (mStatus != other.mStatus) {
             return false;
         }
@@ -480,6 +498,7 @@
         StringBuilder builder = new StringBuilder();
         builder.append("PrinterInfo{");
         builder.append(mId).append(", \"");
+        builder.append(mLabel);
         builder.append("\"}");
         return builder.toString();
     }
@@ -620,14 +639,20 @@
          * Creates a new instance.
          *
          * @param printerId The printer id. Cannot be null.
+         * @param label The human readable printer label. Cannot be null or empty.
          *
          * @throws IllegalArgumentException If the printer id is null.
+         * @throws IllegalArgumentException If the label is empty.
          */
-        public Builder(PrinterId printerId) {
+        public Builder(PrinterId printerId, CharSequence label) {
             if (printerId == null) {
                 throw new IllegalArgumentException("printerId cannot be null.");
             }
+            if (TextUtils.isEmpty(label)) {
+                throw new IllegalArgumentException("label cannot be empty.");
+            }
             mPrototype = new PrinterInfo();
+            mPrototype.mLabel = label;
             mPrototype.mId = printerId;
         }
 
diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl
index f00b37c4..cdde4d8 100644
--- a/core/java/android/printservice/IPrintServiceClient.aidl
+++ b/core/java/android/printservice/IPrintServiceClient.aidl
@@ -29,7 +29,7 @@
 interface IPrintServiceClient {
     List<PrintJobInfo> getPrintJobInfos();
     PrintJobInfo getPrintJobInfo(int printJobId);
-    boolean setPrintJobState(int printJobId, int state, CharSequence error);
+    boolean setPrintJobState(int printJobId, int status);
     boolean setPrintJobTag(int printJobId, String tag);
     oneway void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
 }
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index 64c079e..0ac5a13 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -121,7 +121,7 @@
      */
     public boolean start() {
         if (isQueued()) {
-            return setState(PrintJobInfo.STATE_STARTED, null);
+            return setState(PrintJobInfo.STATE_STARTED);
         }
         return false;
     }
@@ -136,43 +136,41 @@
      */
     public boolean complete() {
         if (isStarted()) {
-            return setState(PrintJobInfo.STATE_COMPLETED, null);
+            return setState(PrintJobInfo.STATE_COMPLETED);
         }
         return false;
     }
 
     /**
      * Fails the print job. You should call this method if {@link
-     * #isQueued()} or {@link #isStarted()} returns true you failed
-     * while printing.
+     * #isStarted()} returns true you filed while printing.
      *
-     * @param error The human readable, short, and translated reason
-     * for the failure.
+     * @param error The reason for the failure.
      * @return Whether the job was failed.
      *
-     * @see #isQueued()
      * @see #isStarted()
      */
     public boolean fail(CharSequence error) {
-        if (isQueued() || isStarted()) {
-            return setState(PrintJobInfo.STATE_FAILED, error);
+        // TODO: Propagate the error message to the UI.
+        if (isStarted()) {
+            return setState(PrintJobInfo.STATE_FAILED);
         }
         return false;
     }
 
     /**
      * 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)}.
+     * #isStarted()} returns true and you canceled the print job as a
+     * response to a call to {@link PrintService#onRequestCancelPrintJob(
+     * PrintJob)}.
      *
      * @return Whether the job as canceled.
      *
      * @see #isStarted()
      */
     public boolean cancel() {
-        if (isQueued() || isStarted()) {
-            return setState(PrintJobInfo.STATE_CANCELED, null);
+        if (isStarted()) {
+            return setState(PrintJobInfo.STATE_CANCELED);
         }
         return false;
     }
@@ -224,14 +222,13 @@
                 || state == PrintJobInfo.STATE_CANCELED;
     }
 
-    private boolean setState(int state, CharSequence error) {
+    private boolean setState(int state) {
         try {
-            if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state, error)) {
+            if (mPrintServiceClient.setPrintJobState(mCachedInfo.getId(), state)) {
                 // Best effort - update the state of the cached info since
                 // 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);
                 return true;
             }
         } catch (RemoteException re) {
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 4d67d15..15e1b73 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -428,14 +428,15 @@
     }
 
     /**
-     * Generates a global printer id given the printer's locally unique name.
+     * Generates a global printer id from a local id. The local id is unique
+     * only within this print service.
      *
-     * @param printerName The printerName.
+     * @param localId The local id.
      * @return Global printer id.
      */
-    public final PrinterId generatePrinterId(String printerName) {
+    public final PrinterId generatePrinterId(String localId) {
         return new PrinterId(new ComponentName(getPackageName(),
-                getClass().getName()), printerName);
+                getClass().getName()), localId);
     }
 
     @Override
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index 4826854..43dd1b6 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -113,22 +113,23 @@
 
                 String nodeName = parser.getName();
                 if (!TAG_PRINT_SERVICE.equals(nodeName)) {
-                    Log.e(LOG_TAG, "Meta-data does not start with " + TAG_PRINT_SERVICE + " tag");
-                } else {
-                    Resources resources = packageManager.getResourcesForApplication(
-                            resolveInfo.serviceInfo.applicationInfo);
-                    AttributeSet allAttributes = Xml.asAttributeSet(parser);
-                    TypedArray attributes = resources.obtainAttributes(allAttributes,
-                            com.android.internal.R.styleable.PrintService);
-
-                    settingsActivityName = attributes.getString(
-                            com.android.internal.R.styleable.PrintService_settingsActivity);
-
-                    addPrintersActivityName = attributes.getString(
-                            com.android.internal.R.styleable.PrintService_addPrintersActivity);
-
-                    attributes.recycle();
+                    throw new XmlPullParserException(
+                            "Meta-data does not start with " + TAG_PRINT_SERVICE + " tag");
                 }
+
+                Resources resources = packageManager.getResourcesForApplication(
+                        resolveInfo.serviceInfo.applicationInfo);
+                AttributeSet allAttributes = Xml.asAttributeSet(parser);
+                TypedArray attributes = resources.obtainAttributes(allAttributes,
+                        com.android.internal.R.styleable.PrintService);
+
+                settingsActivityName = attributes.getString(
+                        com.android.internal.R.styleable.PrintService_settingsActivity);
+
+                addPrintersActivityName = attributes.getString(
+                        com.android.internal.R.styleable.PrintService_addPrintersActivity);
+
+                attributes.recycle();
             } catch (IOException ioe) {
                 Log.w(LOG_TAG, "Error reading meta-data:" + ioe);
             } catch (XmlPullParserException xppe) {
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index 72d064a..f0bbfa4 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -25,8 +25,8 @@
 
     <uses-sdk android:minSdkVersion="17" android:targetSdkVersion="17"/>
 
-    <uses-permission android:name="android.permission.ACCESS_ALL_PRINT_JOBS"/>
-    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL"/>
 
     <permission android:name="android.permission.BIND_PRINT_SPOOLER_SERVICE"
         android:label="@string/permlab_bindPrintSpoolerService"
@@ -50,11 +50,6 @@
             android:theme="@android:style/Theme.Holo.Light.Dialog.NoActionBar">
         </activity>
 
-        <receiver
-            android:name=".NotificationController$NotificationBroadcastReceiver"
-            android:exported="false" >
-        </receiver>
-
     </application>
 
 </manifest>
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index f400f21..1762693 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -55,28 +55,6 @@
     <!-- Title if the number of pages in a printed document is unknown. [CHAR LIMIT=20] -->
     <string name="page_count_unknown">unknown</string>
 
-    <!-- Notifications -->
-
-    <!-- Template for the notificaiton label for a queued print job. [CHAR LIMIT=25] -->
-    <string name="queued_notification_title_template">Queued <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
-
-    <!-- Template for the notificaiton label for a printing print job. [CHAR LIMIT=25] -->
-    <string name="printing_notification_title_template">Printing <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
-
-    <!-- Template for the notificaiton label for a cancelling print job. [CHAR LIMIT=25] -->
-    <string name="cancelling_notification_title_template">Cancelling <xliff:g id="print_job_name" example="foo.jpg">%1$s</xliff:g></string>
-
-    <!-- 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>
-
-    <!-- Label for the notification button for cancelling a print job. [CHAR LIMIT=25] -->
-    <string name="cancel">Cancel</string>
-
-    <!-- Label for the notification button for restrating a filed print job. [CHAR LIMIT=25] -->
-    <string name="restart">Restart</string>
-
-    <!-- Arrays -->
-
     <!-- Color mode labels. -->
     <string-array name="color_mode_labels">
         <!-- Color modelabel: Monochrome color scheme, e.g. one color is used. [CHAR LIMIT=20] -->
diff --git a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java
deleted file mode 100644
index 60a590e..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/NotificationController.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.printspooler;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.os.Build;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.os.UserHandle;
-import android.print.IPrintManager;
-import android.print.PrintJobInfo;
-import android.print.PrintManager;
-import android.util.Log;
-
-/**
- * This class is responsible for updating the print notifications
- * based on print job state transitions.
- */
-public class NotificationController {
-    public static final boolean DEBUG = true && Build.IS_DEBUGGABLE;
-
-    public static final String LOG_TAG = "NotificationController";
-
-    private static final String INTENT_ACTION_CANCEL_PRINTJOB = "INTENT_ACTION_CANCEL_PRINTJOB";
-    private static final String INTENT_ACTION_RESTART_PRINTJOB = "INTENT_ACTION_RESTART_PRINTJOB";
-    private static final String INTENT_EXTRA_PRINTJOB_ID = "INTENT_EXTRA_PRINTJOB_ID";
-
-    private final Context mContext;
-    private final NotificationManager mNotificationManager;
-
-    public NotificationController(Context context) {
-        mContext = context;
-        mNotificationManager = (NotificationManager)
-                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-    }
-
-    public void onPrintJobStateChanged(PrintJobInfo printJob, int oldState) {
-        if (DEBUG) {
-            Log.i(LOG_TAG, "onPrintJobStateChanged() printJobId: " + printJob.getId()
-                    + " oldState: " + PrintJobInfo.stateToString(oldState)
-                    + " newState:" + PrintJobInfo.stateToString(printJob.getState()));
-        }
-        switch (printJob.getState()) {
-            case PrintJobInfo.STATE_QUEUED: {
-                createQueuingNotificaiton(printJob);
-            } break;
-
-            case PrintJobInfo.STATE_STARTED: {
-                createPrintingNotificaiton(printJob);
-            } break;
-
-            case PrintJobInfo.STATE_FAILED: {
-                createFailedNotificaiton(printJob);
-            } break;
-
-            case PrintJobInfo.STATE_COMPLETED:
-            case PrintJobInfo.STATE_CANCELED: {
-                if (oldState > PrintJobInfo.STATE_CREATED) {
-                    removeNotification(printJob.getId());
-                }
-            } break;
-        }
-    }
-
-    private void createQueuingNotificaiton(PrintJobInfo printJob) {
-        Notification.Builder builder = new Notification.Builder(mContext)
-                // TODO: Use appropriate icon when assets are ready
-                .setSmallIcon(android.R.drawable.ic_secure)
-                .setContentTitle(mContext.getString(R.string.queued_notification_title_template,
-                        printJob.getLabel()))
-                // TODO: Use appropriate icon when assets are ready
-                .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel),
-                        createCancelIntent(printJob.getId()))
-                .setContentText(printJob.getPrinterId().getPrinterName())
-                .setOngoing(true)
-                .setWhen(System.currentTimeMillis())
-                .setShowWhen(true);
-        mNotificationManager.notify(printJob.getId(), builder.build());
-    }
-
-    private void createPrintingNotificaiton(PrintJobInfo printJob) {
-        Notification.Builder builder = new Notification.Builder(mContext)
-                // TODO: Use appropriate icon when assets are ready
-                .setSmallIcon(android.R.drawable.ic_secure)
-                .setContentTitle(mContext.getString(R.string.printing_notification_title_template,
-                        printJob.getLabel()))
-                // TODO: Use appropriate icon when assets are ready
-                .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel),
-                        createCancelIntent(printJob.getId()))
-                .setContentText(printJob.getPrinterId().getPrinterName())
-                .setOngoing(true)
-                .setWhen(System.currentTimeMillis())
-                .setShowWhen(true);
-        mNotificationManager.notify(printJob.getId(), builder.build());
-    }
-
-    private void createFailedNotificaiton(PrintJobInfo printJob) {
-        Notification.Builder builder = new Notification.Builder(mContext)
-                // TODO: Use appropriate icon when assets are ready
-                .setSmallIcon(android.R.drawable.ic_secure)
-                .setContentTitle(mContext.getString(R.string.failed_notification_title_template,
-                        printJob.getLabel()))
-                // TODO: Use appropriate icon when assets are ready
-                .addAction(android.R.drawable.ic_secure, mContext.getString(R.string.cancel),
-                        createCancelIntent(printJob.getId()))
-                // 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())
-                .setOngoing(true)
-                .setWhen(System.currentTimeMillis())
-                .setShowWhen(true);
-        mNotificationManager.notify(printJob.getId(), builder.build());
-    }
-
-    private void removeNotification(int printJobId) {
-        mNotificationManager.cancel(printJobId);
-    }
-
-    private PendingIntent createCancelIntent(int printJobId) {
-        Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
-        intent.setAction(INTENT_ACTION_CANCEL_PRINTJOB + "_" + String.valueOf(printJobId));
-        intent.putExtra(INTENT_EXTRA_PRINTJOB_ID, printJobId);
-        return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
-    }
-
-    private PendingIntent createRestartIntent(int printJobId) {
-        Intent intent = new Intent(mContext, NotificationBroadcastReceiver.class);
-        intent.setAction(INTENT_ACTION_RESTART_PRINTJOB + "_" + String.valueOf(printJobId));
-        intent.putExtra(INTENT_EXTRA_PRINTJOB_ID, printJobId);
-        return PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
-    }
-
-    public static final class NotificationBroadcastReceiver extends BroadcastReceiver {
-        private static final String LOG_TAG = "NotificationBroadcastReceiver";
-
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (action != null && action.startsWith(INTENT_ACTION_CANCEL_PRINTJOB)) {
-                final int printJobId = intent.getExtras().getInt(INTENT_EXTRA_PRINTJOB_ID);
-                handleCancelPrintJob(context, printJobId);
-            } else if (action != null && action.startsWith(INTENT_ACTION_RESTART_PRINTJOB)) {
-                final int printJobId = intent.getExtras().getInt(INTENT_EXTRA_PRINTJOB_ID);
-                handleRestartPrintJob(context, printJobId);
-            }
-        }
-
-        private void handleCancelPrintJob(final Context context, final int printJobId) {
-            if (DEBUG) {
-                Log.i(LOG_TAG, "handleCancelPrintJob() printJobId:" + printJobId);
-            }
-
-            PrintSpooler printSpooler = PrintSpooler.getInstance(context);
-
-            final PrintJobInfo printJob = printSpooler.getPrintJobInfo(printJobId,
-                    PrintManager.APP_ID_ANY);
-
-            if (printJob == null || printJob.getState() == PrintJobInfo.STATE_CANCELED) {
-                return;
-            }
-
-            // Put up a notification that we are trying to cancel.
-            NotificationManager notificationManager = (NotificationManager)
-                    context.getSystemService(Context.NOTIFICATION_SERVICE);
-
-            Notification.Builder builder = new Notification.Builder(context)
-                    // TODO: Use appropriate icon when assets are ready
-                    .setSmallIcon(android.R.drawable.ic_secure)
-                    .setContentTitle(context.getString(
-                            R.string.cancelling_notification_title_template,
-                            printJob.getLabel()))
-                    .setContentText(printJob.getPrinterId().getPrinterName())
-                    .setOngoing(true)
-                    .setWhen(System.currentTimeMillis())
-                    .setShowWhen(true);
-
-            notificationManager.notify(printJob.getId(), builder.build());
-
-            // We need to request the cancellation to be done by the print
-            // manager service since it has to communicate with the managing
-            // print service to request the cancellation. Also we need the
-            // system service to be bound to the spooler since canceling a
-            // print job will trigger persistence of current jobs which is
-            // done on another thread and until it finishes the spooler has
-            // to be kept around.
-            IPrintManager printManager = IPrintManager.Stub.asInterface(
-                    ServiceManager.getService(Context.PRINT_SERVICE));
-
-            try {
-                printManager.cancelPrintJob(printJobId, PrintManager.APP_ID_ANY,
-                        UserHandle.myUserId());
-            } catch (RemoteException re) {
-                Log.i(LOG_TAG, "Error requestion print job cancellation", re);
-            }
-        }
-
-        private void handleRestartPrintJob(final Context context, final int printJobId) {
-            if (DEBUG) {
-                Log.i(LOG_TAG, "handleRestartPrintJob() printJobId:" + printJobId);
-            }
-
-            PrintSpooler printSpooler = PrintSpooler.getInstance(context);
-
-            PrintJobInfo printJob = printSpooler.getPrintJobInfo(printJobId,
-                    PrintManager.APP_ID_ANY);
-
-            if (printJob == null || printJob.getState() != PrintJobInfo.STATE_FAILED) {
-                return;
-            }
-
-            // We need to request the restart to be done by the print manager
-            // service since the latter must be bound to the spooler because
-            // restarting a print job will trigger persistence of current jobs
-            // which is done on another thread and until it finishes the spooler has
-            // to be kept around.
-            IPrintManager printManager = IPrintManager.Stub.asInterface(
-                    ServiceManager.getService(Context.PRINT_SERVICE));
-
-            try {
-                printManager.restartPrintJob(printJobId, PrintManager.APP_ID_ANY,
-                        UserHandle.myUserId());
-            } catch (RemoteException re) {
-                Log.i(LOG_TAG, "Error requestion print job restart", re);
-            }
-        }
-    }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index ec93547..d61fd2c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -50,6 +50,7 @@
 import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.view.Choreographer;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.View;
@@ -191,16 +192,16 @@
         mPrinterDiscoveryObserver = null;
         if (mController.isCancelled() || mController.isFailed()) {
             mSpooler.setPrintJobState(mPrintJobId,
-                    PrintJobInfo.STATE_CANCELED, null);
+                    PrintJobInfo.STATE_CANCELED);
         } else if (mController.hasStarted()) {
             mController.finish();
             if (mEditor.isPrintConfirmed()) {
                 if (mController.isFinished()) {
                     mSpooler.setPrintJobState(mPrintJobId,
-                            PrintJobInfo.STATE_QUEUED, null);
+                            PrintJobInfo.STATE_QUEUED);
                 } else {
                     mSpooler.setPrintJobState(mPrintJobId,
-                            PrintJobInfo.STATE_CANCELED, null);
+                            PrintJobInfo.STATE_CANCELED);
                 }
             }
         }
@@ -567,10 +568,6 @@
             @Override
             public void onItemSelected(AdapterView<?> spinner, View view, int position, long id) {
                 if (spinner == mDestinationSpinner) {
-                    if (mIgnoreNextDestinationChange) {
-                        mIgnoreNextDestinationChange = false;
-                        return;
-                    }
                     mCurrPrintAttributes.clear();
                     SpinnerItem<PrinterInfo> dstItem = mDestinationSpinnerAdapter.getItem(position);
                     if (dstItem != null) {
@@ -593,20 +590,12 @@
                     }
                     updateUi();
                 } else if (spinner == mMediaSizeSpinner) {
-                    if (mIgnoreNextMediaSizeChange) {
-                        mIgnoreNextMediaSizeChange = false;
-                        return;
-                    }
                     SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
                     mCurrPrintAttributes.setMediaSize(mediaItem.value);
                     if (!hasErrors()) {
                         mController.update();
                     }
                 } else if (spinner == mColorModeSpinner) {
-                    if (mIgnoreNextColorModeChange) {
-                        mIgnoreNextColorModeChange = false;
-                        return;
-                    }
                     SpinnerItem<Integer> colorModeItem =
                             mColorModeSpinnerAdapter.getItem(position);
                     mCurrPrintAttributes.setColorMode(colorModeItem.value);
@@ -614,10 +603,6 @@
                         mController.update();
                     }
                 } else if (spinner == mOrientationSpinner) {
-                    if (mIgnoreNextOrientationChange) {
-                        mIgnoreNextOrientationChange = false;
-                        return;
-                    }
                     SpinnerItem<Integer> orientationItem =
                             mOrientationSpinnerAdapter.getItem(position);
                     mCurrPrintAttributes.setOrientation(orientationItem.value);
@@ -625,10 +610,6 @@
                         mController.update();
                     }
                 } else if (spinner == mRangeOptionsSpinner) {
-                    if (mIgnoreNextRangeOptionChange) {
-                        mIgnoreNextRangeOptionChange = false;
-                        return;
-                    }
                     updateUi();
                     if (!hasErrors()) {
                         mController.update();
@@ -655,11 +636,6 @@
 
             @Override
             public void afterTextChanged(Editable editable) {
-                if (mIgnoreNextCopiesChange) {
-                    mIgnoreNextCopiesChange = false;
-                    return;
-                }
-
                 final boolean hadErrors = hasErrors();
 
                 if (editable.length() == 0) {
@@ -698,11 +674,6 @@
 
             @Override
             public void afterTextChanged(Editable editable) {
-                if (mIgnoreNextRangeChange) {
-                    mIgnoreNextRangeChange = false;
-                    return;
-                }
-
                 final boolean hadErrors = hasErrors();
 
                 String text = editable.toString();
@@ -745,19 +716,9 @@
 
         private int mEditorState;
 
-        private boolean mIgnoreNextDestinationChange;
-        private boolean mIgnoreNextMediaSizeChange;
-        private boolean mIgnoreNextColorModeChange;
-        private boolean mIgnoreNextOrientationChange;
-        private boolean mIgnoreNextRangeOptionChange;
-        private boolean mIgnoreNextCopiesChange;
-        private boolean mIgnoreNextRangeChange;
-
         public Editor() {
             // Copies
             mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
-            mCopiesEditText.setText(String.valueOf(MIN_COPIES));
-            mSpooler.setPrintJobCopiesNoPersistence(mPrintJobId, MIN_COPIES);
             mCopiesEditText.addTextChangedListener(mCopiesTextWatcher);
             mCopiesEditText.selectAll();
 
@@ -811,12 +772,19 @@
                         rangeOptionsValues[i], rangeOptionsLabels[i]));
             }
             mRangeOptionsSpinner.setAdapter(mRangeOptionsSpinnerAdapter);
-            if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
-                mIgnoreNextRangeOptionChange = true;
-                mRangeOptionsSpinner.setSelection(0);
-            }
+            mRangeOptionsSpinner.setSelection(0);
+            // Here is some voodoo to circumvent the weird behavior of AdapterView
+            // in which a selection listener may get a callback for an event that
+            // happened before the listener was registered. The reason for that is
+            // that the selection change is handled on the next layout pass.
+            Choreographer.getInstance().postCallbackDelayed(Choreographer.CALLBACK_TRAVERSAL,
+                    new Runnable() {
+                @Override
+                public void run() {
+                    mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+                }
+            }, null, Choreographer.getFrameDelay() * 2);
 
-            // Preview button
             mPrintPreviewButton = (Button) findViewById(R.id.print_preview_button);
             mPrintPreviewButton.setOnClickListener(new OnClickListener() {
                 @Override
@@ -829,7 +797,6 @@
                 }
             });
 
-            // Print button
             mPrintButton = (Button) findViewById(R.id.print_button);
             mPrintButton.setOnClickListener(new OnClickListener() {
                 @Override
@@ -839,16 +806,11 @@
                     mController.update();
                 }
             });
-
-            updateUi();
         }
 
         public void initialize() {
             mEditorState = EDITOR_STATE_INITIALIZED;
-            if (mDestinationSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION) {
-                mIgnoreNextDestinationChange = true;
-                mDestinationSpinner.setSelection(AdapterView.INVALID_POSITION);
-            }
+            mDestinationSpinner.setSelection(AdapterView.INVALID_POSITION);
         }
 
         public boolean isCancelled() {
@@ -938,46 +900,35 @@
                 // Destination
                 mDestinationSpinner.setEnabled(false);
 
-                if (!TextUtils.equals(mCopiesEditText.getText(), String.valueOf(MIN_COPIES))) {
-                    mIgnoreNextCopiesChange = true;
-                    mCopiesEditText.setText(String.valueOf(MIN_COPIES));
-                }
+                mCopiesEditText.removeTextChangedListener(mCopiesTextWatcher);
+                mCopiesEditText.setText(String.valueOf(MIN_COPIES));
+                mCopiesEditText.addTextChangedListener(mCopiesTextWatcher);
                 mCopiesEditText.setEnabled(false);
 
                 // Media size
-                if (mMediaSizeSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION) {
-                    mIgnoreNextMediaSizeChange = true;
-                    mMediaSizeSpinner.setSelection(AdapterView.INVALID_POSITION);
-                }
+                mMediaSizeSpinner.setOnItemSelectedListener(null);
+                mMediaSizeSpinner.setSelection(AdapterView.INVALID_POSITION);
                 mMediaSizeSpinner.setEnabled(false);
 
                 // Color mode
-                if (mColorModeSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION) {
-                    mIgnoreNextColorModeChange = true;
-                    mColorModeSpinner.setSelection(AdapterView.INVALID_POSITION);
-                }
+                mColorModeSpinner.setOnItemSelectedListener(null);
+                mColorModeSpinner.setSelection(AdapterView.INVALID_POSITION);
                 mColorModeSpinner.setEnabled(false);
 
                 // Orientation
-                if (mOrientationSpinner.getSelectedItemPosition() != AdapterView.INVALID_POSITION) {
-                    mIgnoreNextOrientationChange = true;
-                    mOrientationSpinner.setSelection(AdapterView.INVALID_POSITION);
-                }
+                mOrientationSpinner.setOnItemSelectedListener(null);
+                mOrientationSpinner.setSelection(AdapterView.INVALID_POSITION);
                 mOrientationSpinner.setEnabled(false);
 
                 // Range
-                if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
-                    mIgnoreNextRangeOptionChange = true;
-                    mRangeOptionsSpinner.setSelection(0);
-                }
+                mRangeOptionsSpinner.setOnItemSelectedListener(null);
+                mRangeOptionsSpinner.setSelection(0);
                 mRangeOptionsSpinner.setEnabled(false);
                 mRangeTitle.setText(getString(R.string.label_pages,
                         getString(R.string.page_count_unknown)));
-                if (!TextUtils.equals(mRangeEditText.getText(), "")) {
-                    mIgnoreNextRangeChange = true;
-                    mRangeEditText.setText("");
-                }
-                
+                mRangeEditText.removeTextChangedListener(mRangeTextWatcher);
+                mRangeEditText.setText("");
+                mRangeEditText.addTextChangedListener(mRangeTextWatcher);
                 mRangeEditText.setEnabled(false);
                 mRangeEditText.setVisibility(View.INVISIBLE);
 
@@ -1023,10 +974,8 @@
                         mMediaSizeSpinner.setEnabled(true);
                         final int selectedMediaSizeIndex = Math.max(mediaSizes.indexOf(
                                 defaultAttributes.getMediaSize()), 0);
-                        if (mMediaSizeSpinner.getSelectedItemPosition() != selectedMediaSizeIndex) {
-                            mIgnoreNextMediaSizeChange = true;
-                            mMediaSizeSpinner.setSelection(selectedMediaSizeIndex);
-                        }
+                        mMediaSizeSpinner.setOnItemSelectedListener(null);
+                        mMediaSizeSpinner.setSelection(selectedMediaSizeIndex);
                     }
                 }
 
@@ -1065,10 +1014,8 @@
                         mColorModeSpinner.setEnabled(true);
                         final int selectedColorModeIndex = Integer.numberOfTrailingZeros(
                                 (colorModes & defaultAttributes.getColorMode()));
-                        if (mColorModeSpinner.getSelectedItemPosition() != selectedColorModeIndex) {
-                            mIgnoreNextColorModeChange = true;
-                            mColorModeSpinner.setSelection(selectedColorModeIndex);
-                        }
+                        mColorModeSpinner.setOnItemSelectedListener(null);
+                        mColorModeSpinner.setSelection(selectedColorModeIndex);
                     }
                 }
 
@@ -1108,11 +1055,8 @@
                         mOrientationSpinner.setEnabled(true);
                         final int selectedOrientationIndex = Integer.numberOfTrailingZeros(
                                 (orientations & defaultAttributes.getOrientation()));
-                        if (mOrientationSpinner.getSelectedItemPosition()
-                                != selectedOrientationIndex) {
-                            mIgnoreNextOrientationChange = true;
-                            mOrientationSpinner.setSelection(selectedOrientationIndex);
-                        }
+                        mOrientationSpinner.setOnItemSelectedListener(null);
+                        mOrientationSpinner.setSelection(selectedOrientationIndex);
                     }
                 }
 
@@ -1136,10 +1080,8 @@
                                     ? getString(R.string.page_count_unknown)
                                     : String.valueOf(pageCount)));
                 } else {
-                    if (mRangeOptionsSpinner.getSelectedItemPosition() != 0) {
-                        mIgnoreNextRangeOptionChange = true;
-                        mRangeOptionsSpinner.setSelection(0);
-                    }
+                    mRangeOptionsSpinner.setOnItemSelectedListener(null);
+                    mRangeOptionsSpinner.setSelection(0);
                     mRangeOptionsSpinner.setEnabled(false);
                     mRangeTitle.setText(getString(R.string.label_pages,
                             getString(R.string.page_count_unknown)));
@@ -1167,12 +1109,26 @@
                 // Copies
                 if (mCopiesEditText.getError() == null
                         && TextUtils.isEmpty(mCopiesEditText.getText())) {
-                    mIgnoreNextCopiesChange = true;
                     mCopiesEditText.setText(String.valueOf(MIN_COPIES));
                     mCopiesEditText.selectAll();
                     mCopiesEditText.requestFocus();
                 }
             }
+
+            // Here is some voodoo to circumvent the weird behavior of AdapterView
+            // in which a selection listener may get a callback for an event that
+            // happened before the listener was registered. The reason for that is
+            // that the selection change is handled on the next layout pass.
+            Choreographer.getInstance().postCallbackDelayed(Choreographer.CALLBACK_TRAVERSAL,
+                    new Runnable() {
+                @Override
+                public void run() {
+                    mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+                    mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+                    mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+                    mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+                }
+            }, null, Choreographer.getFrameDelay() * 2);
         }
 
         public void addPrinters(List<PrinterInfo> addedPrinters) {
@@ -1190,7 +1146,7 @@
                 }
                 if (!duplicate) {
                     mDestinationSpinnerAdapter.add(new SpinnerItem<PrinterInfo>(
-                            addedPrinter, addedPrinter.getId().getPrinterName()));
+                            addedPrinter, addedPrinter.getLabel()));
                 } else {
                     Log.w(LOG_TAG, "Skipping a duplicate printer: " + addedPrinter);
                 }
@@ -1313,14 +1269,14 @@
 
                 PrinterInfo printerInfo = getItem(position).value;
                 TextView title = (TextView) convertView.findViewById(R.id.title);
-                title.setText(printerInfo.getId().getPrinterName());
+                title.setText(printerInfo.getLabel());
 
                 try {
                     TextView subtitle = (TextView)
                             convertView.findViewById(R.id.subtitle);
                     PackageManager pm = getPackageManager();
                     PackageInfo packageInfo = pm.getPackageInfo(
-                            printerInfo.getId().getServiceName().getPackageName(), 0);
+                            printerInfo.getId().getService().getPackageName(), 0);
                     subtitle.setText(packageInfo.applicationInfo.loadLabel(pm));
                     subtitle.setVisibility(View.VISIBLE);
                 } catch (NameNotFoundException nnfe) {
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
index fabd68f..870bfffd 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
@@ -85,8 +85,6 @@
 
     private final PersistenceManager mPersistanceManager;
 
-    private final NotificationController mNotificationController;
-
     private final Handler mHandler;
 
     private final Context mContext;
@@ -105,7 +103,6 @@
     private PrintSpooler(Context context) {
         mContext = context;
         mPersistanceManager = new PersistenceManager(context);
-        mNotificationController = new NotificationController(context);
         mHandler = new MyHandler(context.getMainLooper());
     }
 
@@ -126,7 +123,7 @@
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = mClient;
             args.arg2 = printers;
-            mHandler.obtainMessage(MyHandler.MSG_ON_REQUEST_UPDATE_PRINTERS,
+            mHandler.obtainMessage(MyHandler.MSG_REQUEST_UPDATE_PRINTERS,
                     args).sendToTarget();
         }
     }
@@ -136,14 +133,14 @@
             SomeArgs args = SomeArgs.obtain();
             args.arg1 = mClient;
             args.arg2 = observer;
-            mHandler.obtainMessage(MyHandler.MSG_ON_START_PRINTER_DISCOVERY,
+            mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY,
                     args).sendToTarget();
         }
     }
 
     public void stopPrinterDiscovery() {
         synchronized (mLock) {
-            mHandler.obtainMessage(MyHandler.MSG_ON_STOP_PRINTER_DISCOVERY,
+            mHandler.obtainMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY,
                     mClient).sendToTarget();
         }
     }
@@ -157,7 +154,7 @@
                 PrinterId printerId = printJob.getPrinterId();
                 final boolean sameComponent = (componentName == null
                         || (printerId != null
-                        && componentName.equals(printerId.getServiceName())));
+                        && componentName.equals(printerId.getService())));
                 final boolean sameAppId = appId == PrintManager.APP_ID_ANY
                         || printJob.getAppId() == appId;
                 final boolean sameState = (state == printJob.getState())
@@ -190,6 +187,21 @@
         }
     }
 
+    public boolean cancelPrintJob(int printJobId, int appId) {
+        synchronized (mLock) {
+            PrintJobInfo printJob = getPrintJobInfo(printJobId, appId);
+            if (printJob != null) {
+                switch (printJob.getState()) {
+                    case PrintJobInfo.STATE_CREATED:
+                    case PrintJobInfo.STATE_QUEUED: {
+                        setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED);
+                    } return true;
+                }
+            }
+            return false;
+        }
+    }
+
     public PrintJobInfo createPrintJob(CharSequence label, IPrintClient client,
             PrintAttributes attributes, int appId) {
         synchronized (mLock) {
@@ -228,7 +240,7 @@
 
                     case PrintJobInfo.STATE_QUEUED:
                     case PrintJobInfo.STATE_STARTED: {
-                        ComponentName service = printJob.getPrinterId().getServiceName();
+                        ComponentName service = printJob.getPrinterId().getService();
                         List<PrintJobInfo> jobsPerService = activeJobsPerServiceMap.get(service);
                         if (jobsPerService == null) {
                             jobsPerService = new ArrayList<PrintJobInfo>();
@@ -238,7 +250,7 @@
                     } break;
 
                     default: {
-                        ComponentName service = printJob.getPrinterId().getServiceName();
+                        ComponentName service = printJob.getPrinterId().getService();
                         if (!activeJobsPerServiceMap.containsKey(service)) {
                             activeJobsPerServiceMap.put(service, null);
                         }
@@ -263,7 +275,7 @@
                         SomeArgs args = SomeArgs.obtain();
                         args.arg1 = client;
                         args.arg2 = new PrintJobInfo(printJob);
-                        mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
+                        mHandler.obtainMessage(MyHandler.MSG_PRINT_JOB_QUEUED,
                                 args).sendToTarget();
                     }
                 }
@@ -271,13 +283,13 @@
                 SomeArgs args = SomeArgs.obtain();
                 args.arg1 = client;
                 args.arg2 = service;
-                mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED,
+                mHandler.obtainMessage(MyHandler.MSG_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED,
                         args).sendToTarget();
             }
         }
 
         if (allPrintJobsHandled) {
-            mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED,
+            mHandler.obtainMessage(MyHandler.MSG_ALL_PRINT_JOBS_HANDLED,
                     client).sendToTarget();
         }
     }
@@ -361,7 +373,7 @@
         }
     }
 
-    public boolean setPrintJobState(int printJobId, int state, CharSequence error) {
+    public boolean setPrintJobState(int printJobId, int state) {
         boolean success = false;
 
         synchronized (mLock) {
@@ -370,13 +382,9 @@
             }
 
             PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
-            if (printJob != null) {
+            if (printJob != null && printJob.getState() < state) {
                 success = true;
-
-                final int oldState = printJob.getState();
                 printJob.setState(state);
-                printJob.setFailureReason(error);
-                mNotificationController.onPrintJobStateChanged(printJob, oldState);
 
                 if (DEBUG_PRINT_JOB_LIFECYCLE) {
                     Slog.i(LOG_TAG, "[STATE CHANGED] " + printJob);
@@ -397,18 +405,18 @@
                             return true;
                         }
 
-                        ComponentName service = printerId.getServiceName();
+                        ComponentName service = printerId.getService();
                         if (!hasActivePrintJobsForServiceLocked(service)) {
                             SomeArgs args = SomeArgs.obtain();
                             args.arg1 = mClient;
                             args.arg2 = service;
                             mHandler.obtainMessage(
-                                    MyHandler.MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED,
+                                    MyHandler.MSG_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED,
                                     args).sendToTarget();
                         }
 
                         if (!hasActivePrintJobsLocked()) {
-                            mHandler.obtainMessage(MyHandler.MSG_ON_ALL_PRINT_JOBS_HANDLED,
+                            mHandler.obtainMessage(MyHandler.MSG_ALL_PRINT_JOBS_HANDLED,
                                     mClient).sendToTarget();
                         }
                     } break;
@@ -417,7 +425,7 @@
                         SomeArgs args = SomeArgs.obtain();
                         args.arg1 = mClient;
                         args.arg2 = new PrintJobInfo(printJob);
-                        mHandler.obtainMessage(MyHandler.MSG_ON_PRINT_JOB_QUEUED,
+                        mHandler.obtainMessage(MyHandler.MSG_PRINT_JOB_QUEUED,
                                 args).sendToTarget();
                     } break;
                 }
@@ -447,7 +455,7 @@
         for (int i = 0; i < printJobCount; i++) {
             PrintJobInfo printJob = mPrintJobs.get(i);
             if (!isActiveState(printJob.getState())
-                    && printJob.getPrinterId().getServiceName().equals(service)) {
+                    && printJob.getPrinterId().getService().equals(service)) {
                 return true;
             }
         }
@@ -561,8 +569,8 @@
         private static final String ATTR_FITTING_MODE = "fittingMode";
         private static final String ATTR_ORIENTATION = "orientation";
 
-        private static final String ATTR_PRINTER_NAME = "printerName";
-        private static final String ATTR_SERVICE_NAME = "serviceName";
+        private static final String ATTR_LOCAL_ID = "localId";
+        private static final String ATTR_SERVICE = "service";
 
         private static final String ATTR_WIDTH_MILS = "widthMils";
         private static final String ATTR_HEIGHT_MILS = "heightMils";
@@ -651,8 +659,8 @@
                     PrinterId printerId = printJob.getPrinterId();
                     if (printerId != null) {
                         serializer.startTag(null, TAG_PRINTER_ID);
-                        serializer.attribute(null, ATTR_PRINTER_NAME, printerId.getPrinterName());
-                        serializer.attribute(null, ATTR_SERVICE_NAME, printerId.getServiceName()
+                        serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
+                        serializer.attribute(null, ATTR_SERVICE, printerId.getService()
                                 .flattenToString());
                         serializer.endTag(null, TAG_PRINTER_ID);
                     }
@@ -803,17 +811,17 @@
                 parser.setInput(in, null);
                 parseState(parser);
             } catch (IllegalStateException ise) {
-                Slog.w(LOG_TAG, "Failed parsing ", ise);
+                Slog.w(LOG_TAG, "Failed parsing " + ise);
             } catch (NullPointerException npe) {
-                Slog.w(LOG_TAG, "Failed parsing ", npe);
+                Slog.w(LOG_TAG, "Failed parsing " + npe);
             } catch (NumberFormatException nfe) {
-                Slog.w(LOG_TAG, "Failed parsing ", nfe);
+                Slog.w(LOG_TAG, "Failed parsing " + nfe);
             } catch (XmlPullParserException xppe) {
-                Slog.w(LOG_TAG, "Failed parsing ", xppe);
+                Slog.w(LOG_TAG, "Failed parsing " + xppe);
             } catch (IOException ioe) {
-                Slog.w(LOG_TAG, "Failed parsing ", ioe);
+                Slog.w(LOG_TAG, "Failed parsing " + ioe);
             } catch (IndexOutOfBoundsException iobe) {
-                Slog.w(LOG_TAG, "Failed parsing ", iobe);
+                Slog.w(LOG_TAG, "Failed parsing " + iobe);
             } finally {
                 try {
                     in.close();
@@ -859,16 +867,16 @@
             printJob.setUserId(userId);
             String tag = parser.getAttributeValue(null, ATTR_TAG);
             printJob.setTag(tag);
-            String copies = parser.getAttributeValue(null, ATTR_COPIES);
+            String copies = parser.getAttributeValue(null, ATTR_TAG);
             printJob.setCopies(Integer.parseInt(copies));
 
             parser.next();
 
             skipEmptyTextTags(parser);
             if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) {
-                String localId = parser.getAttributeValue(null, ATTR_PRINTER_NAME);
+                String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
                 ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
-                        null, ATTR_SERVICE_NAME));
+                        null, ATTR_SERVICE));
                 printJob.setPrinterId(new PrinterId(service, localId));
                 parser.next();
                 skipEmptyTextTags(parser);
@@ -1058,12 +1066,12 @@
     }
 
     private final class MyHandler extends Handler {
-        public static final int MSG_ON_START_PRINTER_DISCOVERY = 1;
-        public static final int MSG_ON_STOP_PRINTER_DISCOVERY = 2;
-        public static final int MSG_ON_PRINT_JOB_QUEUED = 3;
-        public static final int MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 4;
-        public static final int MSG_ON_ALL_PRINT_JOBS_HANDLED = 5;
-        public static final int MSG_ON_REQUEST_UPDATE_PRINTERS = 6;
+        public static final int MSG_START_PRINTER_DISCOVERY = 1;
+        public static final int MSG_STOP_PRINTER_DISCOVERY = 2;
+        public static final int MSG_PRINT_JOB_QUEUED = 3;
+        public static final int MSG_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED = 4;
+        public static final int MSG_ALL_PRINT_JOBS_HANDLED = 5;
+        public static final int MSG_REQUEST_UPDATE_PRINTERS = 6;
 
         public MyHandler(Looper looper) {
             super(looper, null, false);
@@ -1073,7 +1081,7 @@
         @SuppressWarnings("unchecked")
         public void handleMessage(Message message) {
             switch (message.what) {
-                case MSG_ON_START_PRINTER_DISCOVERY: {
+                case MSG_START_PRINTER_DISCOVERY: {
                     SomeArgs args = (SomeArgs) message.obj;
                     IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1;
                     IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) args.arg2;
@@ -1087,7 +1095,7 @@
                     }
                 } break;
 
-                case MSG_ON_STOP_PRINTER_DISCOVERY: {
+                case MSG_STOP_PRINTER_DISCOVERY: {
                     IPrintSpoolerClient client = (IPrintSpoolerClient) message.obj;
                     if (client != null) {
                         try {
@@ -1098,7 +1106,7 @@
                     }
                 } break;
 
-                case MSG_ON_PRINT_JOB_QUEUED: {
+                case MSG_PRINT_JOB_QUEUED: {
                     SomeArgs args = (SomeArgs) message.obj;
                     IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1;
                     PrintJobInfo printJob = (PrintJobInfo) args.arg2;
@@ -1112,7 +1120,7 @@
                     }
                 } break;
 
-                case MSG_ON_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
+                case MSG_ALL_PRINT_JOBS_FOR_SERIVICE_HANDLED: {
                     SomeArgs args = (SomeArgs) message.obj;
                     IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1;
                     ComponentName service = (ComponentName) args.arg2;
@@ -1127,7 +1135,7 @@
                     }
                 } break;
 
-                case MSG_ON_ALL_PRINT_JOBS_HANDLED: {
+                case MSG_ALL_PRINT_JOBS_HANDLED: {
                     final IPrintSpoolerClient client = (IPrintSpoolerClient) message.obj;
                     // This has to run on the tread that is persisting the current state
                     // since this call may result in the system unbinding from the spooler
@@ -1146,7 +1154,7 @@
                     }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
                 } break;
 
-                case MSG_ON_REQUEST_UPDATE_PRINTERS: {
+                case MSG_REQUEST_UPDATE_PRINTERS: {
                     SomeArgs args = (SomeArgs) message.obj;
                     IPrintSpoolerClient client = (IPrintSpoolerClient) args.arg1;
                     List<PrinterId> printerIds = (List<PrinterId>) args.arg2;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index 58853f7..5ff2aa6 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -91,6 +91,17 @@
                 }
             }
 
+            @Override
+            public void cancelPrintJob(int printJobId, IPrintSpoolerCallbacks callback,
+                    int appId, int sequence) throws RemoteException {
+                boolean success = false;
+                try {
+                    success = mSpooler.cancelPrintJob(printJobId, appId);
+                } finally {
+                    callback.onCancelPrintJobResult(success, sequence);
+                }
+            }
+
             @SuppressWarnings("deprecation")
             @Override
             public void createPrintJob(String printJobName, IPrintClient client,
@@ -124,11 +135,14 @@
             }
 
             @Override
-            public void setPrintJobState(int printJobId, int state, CharSequence error,
-                    IPrintSpoolerCallbacks callback, int sequece) throws RemoteException {
+            public void setPrintJobState(int printJobId, int state,
+                    IPrintSpoolerCallbacks callback, int sequece)
+                            throws RemoteException {
                 boolean success = false;
                 try {
-                    success = mSpooler.setPrintJobState(printJobId, state, error);
+                    // TODO: Make sure the clients (print services) can set the state
+                    //       only to acceptable ones, e.g. not settings STATE_CREATED.
+                    success = mSpooler.setPrintJobState(printJobId, state);
                 } finally {
                     callback.onSetPrintJobStateResult(success, sequece);
                 }
diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java
index 41399d8..86e7685 100644
--- a/services/java/com/android/server/print/PrintManagerService.java
+++ b/services/java/com/android/server/print/PrintManagerService.java
@@ -144,45 +144,22 @@
         }
         final long identity = Binder.clearCallingIdentity();
         try {
+            if (spooler.cancelPrintJob(printJobId, resolvedAppId)) {
+                return;
+            }
             PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
             if (printJobInfo == null) {
                 return;
             }
-            if (printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
-                ComponentName printServiceName = printJobInfo.getPrinterId().getServiceName();
-                RemotePrintService printService = null;
-                synchronized (mLock) {
-                    printService = userState.getActiveServices().get(printServiceName);
-                }
-                if (printService == null) {
-                    return;
-                }
-                printService.onRequestCancelPrintJob(printJobInfo);
-            } else {
-                // If the print job is failed we do not need cooperation
-                // from the print service.
-                spooler.setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED, null);
+            ComponentName printServiceName = printJobInfo.getPrinterId().getService();
+            RemotePrintService printService = null;
+            synchronized (mLock) {
+                printService = userState.getActiveServices().get(printServiceName);
             }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    @Override
-    public void restartPrintJob(int printJobId, int appId, int userId) {
-        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
-        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
-        final RemotePrintSpooler spooler;
-        synchronized (mLock) {
-            spooler = getOrCreateUserStateLocked(resolvedUserId).getSpoolerLocked();
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
-            if (printJobInfo == null || printJobInfo.getState() != PrintJobInfo.STATE_FAILED) {
+            if (printService == null) {
                 return;
             }
-            spooler.setPrintJobState(printJobId, PrintJobInfo.STATE_QUEUED, null);
+            printService.onRequestCancelPrintJob(printJobInfo);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java
index 5aa9952..7acf6ab 100644
--- a/services/java/com/android/server/print/RemotePrintService.java
+++ b/services/java/com/android/server/print/RemotePrintService.java
@@ -423,12 +423,12 @@
         }
 
         @Override
-        public boolean setPrintJobState(int printJobId, int state, CharSequence error) {
+        public boolean setPrintJobState(int printJobId, int state) {
             RemotePrintService service = mWeakService.get();
             if (service != null) {
                 final long identity = Binder.clearCallingIdentity();
                 try {
-                    return service.mSpooler.setPrintJobState(printJobId, state, error);
+                    return service.mSpooler.setPrintJobState(printJobId, state);
                 } finally {
                     Binder.restoreCallingIdentity(identity);
                 }
@@ -524,8 +524,8 @@
         }
 
         private void throwIfPrinterIdTampered(PrinterId printerId) {
-            if (printerId == null || printerId.getServiceName() == null
-                    || !printerId.getServiceName().equals(mComponentName)) {
+            if (printerId == null || printerId.getService() == null
+                    || !printerId.getService().equals(mComponentName)) {
                 throw new IllegalArgumentException("Invalid printer id: " + printerId);
             }
         }
diff --git a/services/java/com/android/server/print/RemotePrintSpooler.java b/services/java/com/android/server/print/RemotePrintSpooler.java
index 4e561bb..fe5b067 100644
--- a/services/java/com/android/server/print/RemotePrintSpooler.java
+++ b/services/java/com/android/server/print/RemotePrintSpooler.java
@@ -66,6 +66,8 @@
 
     private final CreatePrintJobCaller mCreatePrintJobCaller = new CreatePrintJobCaller();
 
+    private final CancelPrintJobCaller mCancelPrintJobCaller = new CancelPrintJobCaller();
+
     private final GetPrintJobInfoCaller mGetPrintJobInfoCaller = new GetPrintJobInfoCaller();
 
     private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller();
@@ -161,6 +163,31 @@
         return null;
     }
 
+    public final boolean cancelPrintJob(int printJobId, int appId) {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            mCanUnbind = false;
+        }
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] cancelPrintJob()");
+        }
+        try {
+            return mCancelPrintJobCaller.cancelPrintJob(getRemoteInstanceLazy(),
+                    printJobId, appId);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error canceling print job.", re);
+        } catch (TimeoutException te) {
+            Slog.e(LOG_TAG, "Error canceling print job.", te);
+        } finally {
+            synchronized (mLock) {
+                mCanUnbind = true;
+                mLock.notifyAll();
+            }
+        }
+        return false;
+    }
+
     public final void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
         throwIfCalledOnMainThread();
         synchronized (mLock) {
@@ -212,7 +239,7 @@
         return null;
     }
 
-    public final boolean setPrintJobState(int printJobId, int state, CharSequence error) {
+    public final boolean setPrintJobState(int printJobId, int state) {
         throwIfCalledOnMainThread();
         synchronized (mLock) {
             throwIfDestroyedLocked();
@@ -223,7 +250,7 @@
         }
         try {
             return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstanceLazy(),
-                    printJobId, state, error);
+                    printJobId, state);
         } catch (RemoteException re) {
             Slog.e(LOG_TAG, "Error setting print job state.", re);
         } catch (TimeoutException te) {
@@ -459,6 +486,27 @@
         }
     }
 
+    private static final class CancelPrintJobCaller extends TimedRemoteCaller<Boolean> {
+        private final IPrintSpoolerCallbacks mCallback;
+
+        public CancelPrintJobCaller() {
+            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+            mCallback = new BasePrintSpoolerServiceCallbacks() {
+                @Override
+                public void onCancelPrintJobResult(boolean canceled, int sequence) {
+                    onRemoteMethodResult(canceled, sequence);
+                }
+            };
+        }
+
+        public boolean cancelPrintJob(IPrintSpooler target, int printJobId,
+                int appId) throws RemoteException, TimeoutException {
+            final int sequence = onBeforeRemoteCall();
+            target.cancelPrintJob(printJobId, mCallback, appId, sequence);
+            return getResultTimed(sequence);
+        }
+    }
+
     private static final class GetPrintJobInfoCaller extends TimedRemoteCaller<PrintJobInfo> {
         private final IPrintSpoolerCallbacks mCallback;
 
@@ -494,9 +542,9 @@
         }
 
         public boolean setPrintJobState(IPrintSpooler target, int printJobId,
-                int status, CharSequence error) throws RemoteException, TimeoutException {
+                int status) throws RemoteException, TimeoutException {
             final int sequence = onBeforeRemoteCall();
-            target.setPrintJobState(printJobId, status, error, mCallback, sequence);
+            target.setPrintJobState(printJobId, status, mCallback, sequence);
             return getResultTimed(sequence);
         }
     }
diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java
index c4fe124..c41f9b0 100644
--- a/services/java/com/android/server/print/UserState.java
+++ b/services/java/com/android/server/print/UserState.java
@@ -85,7 +85,7 @@
         final RemotePrintService service;
         synchronized (mLock) {
             throwIfDestroyedLocked();
-            ComponentName printServiceName = printJob.getPrinterId().getServiceName();
+            ComponentName printServiceName = printJob.getPrinterId().getService();
             service = mActiveServices.get(printServiceName);
         }
         if (service != null) {
@@ -147,7 +147,7 @@
             if (mActiveServices.isEmpty()) {
                 return;
             }
-            service = mActiveServices.get(printerIds.get(0).getServiceName());
+            service = mActiveServices.get(printerIds.get(0).getService());
         }
         if (service != null) {
             service.onRequestUpdatePrinters(printerIds);