Merge "Fix missing lock in power manager."
diff --git a/Android.mk b/Android.mk
index db6a704..d81e581 100644
--- a/Android.mk
+++ b/Android.mk
@@ -159,13 +159,15 @@
 	core/java/android/os/IUserManager.aidl \
 	core/java/android/os/IVibratorService.aidl \
 	core/java/android/service/notification/INotificationListener.aidl \
+	core/java/android/print/ILayoutResultCallback.aidl \
+	core/java/android/print/IPrintDocumentAdapter.aidl \
 	core/java/android/print/IPrinterDiscoveryObserver.aidl \
-	core/java/android/print/IPrintAdapter.aidl \
 	core/java/android/print/IPrintClient.aidl \
-	core/java/android/print/IPrintResultCallback.aidl \
 	core/java/android/print/IPrintManager.aidl \
-	core/java/android/print/IPrintSpoolerService.aidl \
-	core/java/android/print/IPrintSpoolerServiceCallbacks.aidl \
+	core/java/android/print/IPrintSpooler.aidl \
+	core/java/android/print/IPrintSpoolerCallbacks.aidl \
+	core/java/android/print/IPrintSpoolerClient.aidl \
+	core/java/android/print/IWriteResultCallback.aidl \
 	core/java/android/printservice/IPrintService.aidl \
 	core/java/android/printservice/IPrintServiceClient.aidl \
 	core/java/android/service/dreams/IDreamManager.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 893daf1..58de3f3 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -163,6 +163,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/framework-res_intermediates)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
 # ************************************************
diff --git a/api/current.txt b/api/current.txt
index 201dc58..e0025ea 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18448,36 +18448,6 @@
     field public static final android.os.Parcelable.Creator CREATOR;
   }
 
-  public abstract class PrintAdapter {
-    ctor public PrintAdapter();
-    method public abstract android.print.PrintAdapterInfo getInfo();
-    method public void onFinish();
-    method public abstract void onPrint(java.util.List<android.print.PageRange>, java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintAdapter.PrintResultCallback);
-    method public boolean onPrintAttributesChanged(android.print.PrintAttributes);
-    method public void onStart();
-  }
-
-  public static abstract class PrintAdapter.PrintResultCallback {
-    method public void onPrintFailed(java.lang.CharSequence);
-    method public void onPrintFinished(java.util.List<android.print.PageRange>);
-  }
-
-  public final class PrintAdapterInfo implements android.os.Parcelable {
-    method public int describeContents();
-    method public int getFlags();
-    method public int getPageCount();
-    method public void writeToParcel(android.os.Parcel, int);
-    field public static final android.os.Parcelable.Creator CREATOR;
-    field public static final int PAGE_COUNT_UNKNOWN = -1; // 0xffffffff
-  }
-
-  public static final class PrintAdapterInfo.Builder {
-    ctor public PrintAdapterInfo.Builder();
-    method public android.print.PrintAdapterInfo create();
-    method public android.print.PrintAdapterInfo.Builder setFlags(int);
-    method public android.print.PrintAdapterInfo.Builder setPageCount(int);
-  }
-
   public final class PrintAttributes implements android.os.Parcelable {
     method public void clear();
     method public int describeContents();
@@ -18528,54 +18498,55 @@
   }
 
   public static final class PrintAttributes.MediaSize {
-    ctor public PrintAttributes.MediaSize(java.lang.String, java.lang.String, int, int, int);
+    ctor public PrintAttributes.MediaSize(java.lang.String, java.lang.CharSequence, int, int);
+    method public static android.print.PrintAttributes.MediaSize createMediaSize(android.content.pm.PackageManager, int);
     method public int getHeightMils();
     method public java.lang.String getId();
-    method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
+    method public java.lang.CharSequence getLabel();
     method public int getWidthMils();
-    field public static final android.print.PrintAttributes.MediaSize ISO_A0;
-    field public static final android.print.PrintAttributes.MediaSize ISO_A1;
-    field public static final android.print.PrintAttributes.MediaSize ISO_A10;
-    field public static final android.print.PrintAttributes.MediaSize ISO_A2;
-    field public static final android.print.PrintAttributes.MediaSize ISO_A3;
-    field public static final android.print.PrintAttributes.MediaSize ISO_A4;
-    field public static final android.print.PrintAttributes.MediaSize ISO_A5;
-    field public static final android.print.PrintAttributes.MediaSize ISO_A6;
-    field public static final android.print.PrintAttributes.MediaSize ISO_A7;
-    field public static final android.print.PrintAttributes.MediaSize ISO_A8;
-    field public static final android.print.PrintAttributes.MediaSize ISO_A9;
-    field public static final android.print.PrintAttributes.MediaSize ISO_B0;
-    field public static final android.print.PrintAttributes.MediaSize ISO_B1;
-    field public static final android.print.PrintAttributes.MediaSize ISO_B10;
-    field public static final android.print.PrintAttributes.MediaSize ISO_B2;
-    field public static final android.print.PrintAttributes.MediaSize ISO_B3;
-    field public static final android.print.PrintAttributes.MediaSize ISO_B4;
-    field public static final android.print.PrintAttributes.MediaSize ISO_B5;
-    field public static final android.print.PrintAttributes.MediaSize ISO_B6;
-    field public static final android.print.PrintAttributes.MediaSize ISO_B7;
-    field public static final android.print.PrintAttributes.MediaSize ISO_B8;
-    field public static final android.print.PrintAttributes.MediaSize ISO_B9;
-    field public static final android.print.PrintAttributes.MediaSize ISO_C0;
-    field public static final android.print.PrintAttributes.MediaSize ISO_C1;
-    field public static final android.print.PrintAttributes.MediaSize ISO_C10;
-    field public static final android.print.PrintAttributes.MediaSize ISO_C2;
-    field public static final android.print.PrintAttributes.MediaSize ISO_C3;
-    field public static final android.print.PrintAttributes.MediaSize ISO_C4;
-    field public static final android.print.PrintAttributes.MediaSize ISO_C5;
-    field public static final android.print.PrintAttributes.MediaSize ISO_C6;
-    field public static final android.print.PrintAttributes.MediaSize ISO_C7;
-    field public static final android.print.PrintAttributes.MediaSize ISO_C8;
-    field public static final android.print.PrintAttributes.MediaSize ISO_C9;
-    field public static final android.print.PrintAttributes.MediaSize NA_GOVT_LETTER;
-    field public static final android.print.PrintAttributes.MediaSize NA_JUNIOR_LEGAL;
-    field public static final android.print.PrintAttributes.MediaSize NA_LEDGER;
-    field public static final android.print.PrintAttributes.MediaSize NA_LEGAL;
-    field public static final android.print.PrintAttributes.MediaSize NA_LETTER;
-    field public static final android.print.PrintAttributes.MediaSize NA_TBLOID;
+    field public static final int ISO_A0 = 1; // 0x1
+    field public static final int ISO_A1 = 2; // 0x2
+    field public static final int ISO_A10 = 11; // 0xb
+    field public static final int ISO_A2 = 3; // 0x3
+    field public static final int ISO_A3 = 4; // 0x4
+    field public static final int ISO_A4 = 5; // 0x5
+    field public static final int ISO_A5 = 6; // 0x6
+    field public static final int ISO_A6 = 7; // 0x7
+    field public static final int ISO_A7 = 8; // 0x8
+    field public static final int ISO_A8 = 9; // 0x9
+    field public static final int ISO_A9 = 10; // 0xa
+    field public static final int ISO_B0 = 100; // 0x64
+    field public static final int ISO_B1 = 101; // 0x65
+    field public static final int ISO_B10 = 110; // 0x6e
+    field public static final int ISO_B2 = 102; // 0x66
+    field public static final int ISO_B3 = 103; // 0x67
+    field public static final int ISO_B4 = 104; // 0x68
+    field public static final int ISO_B5 = 105; // 0x69
+    field public static final int ISO_B6 = 106; // 0x6a
+    field public static final int ISO_B7 = 107; // 0x6b
+    field public static final int ISO_B8 = 108; // 0x6c
+    field public static final int ISO_B9 = 109; // 0x6d
+    field public static final int ISO_C0 = 200; // 0xc8
+    field public static final int ISO_C1 = 201; // 0xc9
+    field public static final int ISO_C10 = 210; // 0xd2
+    field public static final int ISO_C2 = 202; // 0xca
+    field public static final int ISO_C3 = 203; // 0xcb
+    field public static final int ISO_C4 = 204; // 0xcc
+    field public static final int ISO_C5 = 205; // 0xcd
+    field public static final int ISO_C6 = 206; // 0xce
+    field public static final int ISO_C7 = 207; // 0xcf
+    field public static final int ISO_C8 = 208; // 0xd0
+    field public static final int ISO_C9 = 209; // 0xd1
+    field public static final int NA_GOVT_LETTER = 301; // 0x12d
+    field public static final int NA_JUNIOR_LEGAL = 303; // 0x12f
+    field public static final int NA_LEDGER = 304; // 0x130
+    field public static final int NA_LEGAL = 302; // 0x12e
+    field public static final int NA_LETTER = 300; // 0x12c
+    field public static final int NA_TBLOID = 305; // 0x131
   }
 
   public static final class PrintAttributes.Resolution {
-    ctor public PrintAttributes.Resolution(java.lang.String, java.lang.String, int, int, int);
+    ctor public PrintAttributes.Resolution(java.lang.String, java.lang.CharSequence, int, int);
     method public int getHorizontalDpi();
     method public java.lang.String getId();
     method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
@@ -18583,11 +18554,48 @@
   }
 
   public static final class PrintAttributes.Tray {
-    ctor public PrintAttributes.Tray(java.lang.String, java.lang.String, int);
+    ctor public PrintAttributes.Tray(java.lang.String, java.lang.CharSequence);
     method public java.lang.String getId();
     method public java.lang.CharSequence getLabel(android.content.pm.PackageManager);
   }
 
+  public abstract class PrintDocumentAdapter {
+    ctor public PrintDocumentAdapter();
+    method public void onFinish();
+    method public abstract void onLayout(android.print.PrintAttributes, android.print.PrintAttributes, android.os.CancellationSignal, android.print.PrintDocumentAdapter.LayoutResultCallback);
+    method public void onStart();
+    method public abstract void onWrite(java.util.List<android.print.PageRange>, java.io.FileDescriptor, android.os.CancellationSignal, android.print.PrintDocumentAdapter.WriteResultCallback);
+  }
+
+  public static abstract class PrintDocumentAdapter.LayoutResultCallback {
+    method public void onLayoutFailed(java.lang.CharSequence);
+    method public void onLayoutFinished(android.print.PrintDocumentInfo, boolean);
+  }
+
+  public static abstract class PrintDocumentAdapter.WriteResultCallback {
+    method public void onWriteFailed(java.lang.CharSequence);
+    method public void onWriteFinished(java.util.List<android.print.PageRange>);
+  }
+
+  public final class PrintDocumentInfo implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getContentType();
+    method public int getPageCount();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int CONTENT_TYPE_DOCUMENT = 0; // 0x0
+    field public static final int CONTENT_TYPE_PHOTO = 1; // 0x1
+    field public static final int CONTENT_TYPE_UNKNOWN = -1; // 0xffffffff
+    field public static final android.os.Parcelable.Creator CREATOR;
+    field public static final int PAGE_COUNT_UNKNOWN = -1; // 0xffffffff
+  }
+
+  public static final class PrintDocumentInfo.Builder {
+    ctor public PrintDocumentInfo.Builder();
+    method public android.print.PrintDocumentInfo create();
+    method public android.print.PrintDocumentInfo.Builder setContentType(int);
+    method public android.print.PrintDocumentInfo.Builder setPageCount(int);
+  }
+
   public final class PrintJob {
     method public void cancel();
     method public int getId();
@@ -18617,7 +18625,7 @@
   public final class PrintManager {
     method public java.util.List<android.print.PrintJob> getPrintJobs();
     method public android.print.PrintJob print(java.lang.String, java.io.File, android.print.PrintAttributes);
-    method public android.print.PrintJob print(java.lang.String, android.print.PrintAdapter, android.print.PrintAttributes);
+    method public android.print.PrintJob print(java.lang.String, android.print.PrintDocumentAdapter, android.print.PrintAttributes);
   }
 
   public final class PrinterId implements android.os.Parcelable {
@@ -18699,11 +18707,16 @@
 
 package android.printservice {
 
+  public final class PrintDocument {
+    method public java.io.FileDescriptor getData();
+    method public android.print.PrintDocumentInfo getInfo();
+  }
+
   public final class PrintJob {
     method public boolean cancel();
     method public boolean complete();
     method public boolean fail(java.lang.CharSequence);
-    method public final java.io.FileDescriptor getData();
+    method public android.printservice.PrintDocument getDocument();
     method public int getId();
     method public android.print.PrintJobInfo getInfo();
     method public boolean isQueued();
@@ -27034,6 +27047,7 @@
     field protected static final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET;
     field protected static final int[] PRESSED_SELECTED_STATE_SET;
     field protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
+    field protected static final int[] PRESSED_STATE_SET;
     field protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET;
     field public static final android.util.Property ROTATION;
     field public static final android.util.Property ROTATION_X;
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index c79768e..4e6c3dc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -234,32 +234,49 @@
     /** @hide Process is a persistent system process and is doing UI. */
     public static final int PROCESS_STATE_PERSISTENT_UI = 1;
 
-    /** @hide Process is hosting the current top activity. */
+    /** @hide Process is hosting the current top activities.  Note that this covers
+     * all activities that are visible to the user. */
     public static final int PROCESS_STATE_TOP = 2;
 
     /** @hide Process is important to the user, and something they are aware of. */
-    public static final int PROCESS_STATE_IMPORTANT_PERCEPTIBLE = 3;
+    public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 3;
 
     /** @hide Process is important to the user, but not something they are aware of. */
     public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 4;
 
-    /** @hide Process is in the background running a receiver. */
-    public static final int PROCESS_STATE_RECEIVER = 5;
-
     /** @hide Process is in the background running a backup/restore operation. */
-    public static final int PROCESS_STATE_BACKUP = 6;
+    public static final int PROCESS_STATE_BACKUP = 5;
 
-    /** @hide Process is in the background running a service. */
+    /** @hide Process is in the background, but it can't restore its state so we want
+     * to try to avoid killing it. */
+    public static final int PROCESS_STATE_HEAVY_WEIGHT = 6;
+
+    /** @hide Process is in the background running a service.  Unlike oom_adj, this level
+     * is used for both the normal running in background state and the executing
+     * operations state. */
     public static final int PROCESS_STATE_SERVICE = 7;
 
+    /** @hide Process is in the background running a receiver.   Note that from the
+     * perspective of oom_adj receivers run at a higher foreground level, but for our
+     * prioritization here that is not necessary and putting them below services means
+     * many fewer changes in some process states as they receive broadcasts. */
+    public static final int PROCESS_STATE_RECEIVER = 8;
+
     /** @hide Process is in the background but hosts the home activity. */
-    public static final int PROCESS_STATE_HOME = 8;
+    public static final int PROCESS_STATE_HOME = 9;
 
     /** @hide Process is in the background but hosts the last shown activity. */
-    public static final int PROCESS_STATE_LAST_ACTIVITY = 9;
+    public static final int PROCESS_STATE_LAST_ACTIVITY = 10;
 
-    /** @hide Process is being cached for later use. */
-    public static final int PROCESS_STATE_CACHED = 10;
+    /** @hide Process is being cached for later use and contains activities. */
+    public static final int PROCESS_STATE_CACHED_ACTIVITY = 11;
+
+    /** @hide Process is being cached for later use and is a client of another cached
+     * process that contains activities. */
+    public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12;
+
+    /** @hide Process is being cached for later use and is empty. */
+    public static final int PROCESS_STATE_CACHED_EMPTY = 13;
 
     /*package*/ ActivityManager(Context context, Handler handler) {
         mContext = context;
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index c3d1971..aa326ad 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -230,7 +230,15 @@
      * tries to balance such requests from one app vs. the importantance of
      * keeping other apps around.
      */
-    public static final int BIND_VISIBLE = 0x0100;
+    public static final int BIND_VISIBLE = 0x10000000;
+
+    /**
+     * @hide
+     * Flag for {@link #bindService}: Consider this binding to be causing the target
+     * process to be showing UI, so it will be do a UI_HIDDEN memory trim when it goes
+     * away.
+     */
+    public static final int BIND_SHOWING_UI = 0x20000000;
 
     /**
      * Flag for {@link #bindService}: Don't consider the bound service to be
diff --git a/core/java/android/hardware/photography/CameraPropertiesKeys.java b/core/java/android/hardware/photography/CameraPropertiesKeys.java
index 7e6380e..f511ae7 100644
--- a/core/java/android/hardware/photography/CameraPropertiesKeys.java
+++ b/core/java/android/hardware/photography/CameraPropertiesKeys.java
@@ -39,11 +39,8 @@
  **/
 public final class CameraPropertiesKeys {
     public static final class Control {
-
         public static final Key<byte[]> AE_AVAILABLE_ANTIBANDING_MODES =
                 new Key<byte[]>("android.control.aeAvailableAntibandingModes", byte[].class);
-        public static final Key<byte[]> AE_AVAILABLE_MODES =
-                new Key<byte[]>("android.control.aeAvailableModes", byte[].class);
         public static final Key<int[]> AE_AVAILABLE_TARGET_FPS_RANGES =
                 new Key<int[]>("android.control.aeAvailableTargetFpsRanges", int[].class);
         public static final Key<int[]> AE_COMPENSATION_RANGE =
@@ -62,40 +59,23 @@
                 new Key<byte[]>("android.control.awbAvailableModes", byte[].class);
         public static final Key<Integer> MAX_REGIONS =
                 new Key<Integer>("android.control.maxRegions", int.class);
-        public static final Key<byte[]> SCENE_MODE_OVERRIDES =
-                new Key<byte[]>("android.control.sceneModeOverrides", byte[].class);
 
     }
+
     public static final class Flash {
         public static final class Info {
             public static final Key<Byte> AVAILABLE =
                     new Key<Byte>("android.flash.info.available", byte.class);
-            public static final Key<Long> CHARGE_DURATION =
-                    new Key<Long>("android.flash.info.chargeDuration", long.class);
         }
 
-        public static final Key<Byte> COLOR_TEMPERATURE =
-                new Key<Byte>("android.flash.colorTemperature", byte.class);
-        public static final Key<Byte> MAX_ENERGY =
-                new Key<Byte>("android.flash.maxEnergy", byte.class);
-
     }
-    public static final class HotPixel {
-        public static final class Info {
-            public static final Key<int[]> MAP =
-                    new Key<int[]>("android.hotPixel.info.map", int[].class);
-        }
 
-
-    }
     public static final class Jpeg {
-
         public static final Key<int[]> AVAILABLE_THUMBNAIL_SIZES =
                 new Key<int[]>("android.jpeg.availableThumbnailSizes", int[].class);
-        public static final Key<Integer> MAX_SIZE =
-                new Key<Integer>("android.jpeg.maxSize", int.class);
 
     }
+
     public static final class Lens {
         public static final class Info {
             public static final Key<float[]> AVAILABLE_APERTURES =
@@ -106,21 +86,14 @@
                     new Key<float[]>("android.lens.info.availableFocalLengths", float[].class);
             public static final Key<byte[]> AVAILABLE_OPTICAL_STABILIZATION =
                     new Key<byte[]>("android.lens.info.availableOpticalStabilization", byte[].class);
-            public static final Key<float[]> GEOMETRIC_CORRECTION_MAP =
-                    new Key<float[]>("android.lens.info.geometricCorrectionMap", float[].class);
-            public static final Key<int[]> GEOMETRIC_CORRECTION_MAP_SIZE =
-                    new Key<int[]>("android.lens.info.geometricCorrectionMapSize", int[].class);
             public static final Key<Float> HYPERFOCAL_DISTANCE =
                     new Key<Float>("android.lens.info.hyperfocalDistance", float.class);
             public static final Key<Float> MINIMUM_FOCUS_DISTANCE =
                     new Key<Float>("android.lens.info.minimumFocusDistance", float.class);
-            public static final Key<float[]> SHADING_MAP =
-                    new Key<float[]>("android.lens.info.shadingMap", float[].class);
             public static final Key<int[]> SHADING_MAP_SIZE =
                     new Key<int[]>("android.lens.info.shadingMapSize", int[].class);
         }
 
-
             public static final class FacingKey extends Key<Lens.FacingKey.Enum> {
                 public enum Enum {
                     FRONT,
@@ -139,32 +112,16 @@
 
         public static final Key<Lens.FacingKey.Enum> FACING =
                 new FacingKey("android.lens.facing");
-        public static final Key<float[]> OPTICAL_AXIS_ANGLE =
-                new Key<float[]>("android.lens.opticalAxisAngle", float[].class);
-        public static final Key<float[]> POSITION =
-                new Key<float[]>("android.lens.position", float[].class);
 
     }
-    public static final class Quirks {
 
-        public static final Key<Byte> METERING_CROP_REGION =
-                new Key<Byte>("android.quirks.meteringCropRegion", byte.class);
-        public static final Key<Byte> TRIGGER_AF_WITH_AUTO =
-                new Key<Byte>("android.quirks.triggerAfWithAuto", byte.class);
-        public static final Key<Byte> USE_ZSL_FORMAT =
-                new Key<Byte>("android.quirks.useZslFormat", byte.class);
-
-    }
     public static final class Request {
-
         public static final Key<int[]> MAX_NUM_OUTPUT_STREAMS =
                 new Key<int[]>("android.request.maxNumOutputStreams", int[].class);
-        public static final Key<int[]> MAX_NUM_REPROCESS_STREAMS =
-                new Key<int[]>("android.request.maxNumReprocessStreams", int[].class);
 
     }
-    public static final class Scaler {
 
+    public static final class Scaler {
 
             public static final class AvailableFormatsKey extends Key<Scaler.AvailableFormatsKey.Enum[]> {
                 public enum Enum {
@@ -212,184 +169,55 @@
                 new Key<long[]>("android.scaler.availableProcessedMinDurations", long[].class);
         public static final Key<int[]> AVAILABLE_PROCESSED_SIZES =
                 new Key<int[]>("android.scaler.availableProcessedSizes", int[].class);
-        public static final Key<long[]> AVAILABLE_RAW_MIN_DURATIONS =
-                new Key<long[]>("android.scaler.availableRawMinDurations", long[].class);
-        public static final Key<int[]> AVAILABLE_RAW_SIZES =
-                new Key<int[]>("android.scaler.availableRawSizes", int[].class);
 
     }
+
     public static final class Sensor {
         public static final class Info {
             public static final Key<int[]> ACTIVE_ARRAY_SIZE =
                     new Key<int[]>("android.sensor.info.activeArraySize", int[].class);
             public static final Key<int[]> AVAILABLE_SENSITIVITIES =
                     new Key<int[]>("android.sensor.info.availableSensitivities", int[].class);
-
-            public static final class ColorFilterArrangementKey extends Key<Sensor.Info.ColorFilterArrangementKey.Enum> {
-                public enum Enum {
-                    RGGB,
-                    GRBG,
-                    GBRG,
-                    BGGR,
-                    RGB;
-                }
-
-                public static final Enum RGGB = Enum.RGGB;
-                public static final Enum GRBG = Enum.GRBG;
-                public static final Enum GBRG = Enum.GBRG;
-                public static final Enum BGGR = Enum.BGGR;
-                public static final Enum RGB = Enum.RGB;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private ColorFilterArrangementKey(String name) {
-                    super(name, Sensor.Info.ColorFilterArrangementKey.Enum.class);
-                }
-
-            }
-
-            public static final Key<Sensor.Info.ColorFilterArrangementKey.Enum> COLOR_FILTER_ARRANGEMENT =
-                    new ColorFilterArrangementKey("android.sensor.info.colorFilterArrangement");
             public static final Key<long[]> EXPOSURE_TIME_RANGE =
                     new Key<long[]>("android.sensor.info.exposureTimeRange", long[].class);
             public static final Key<Long> MAX_FRAME_DURATION =
                     new Key<Long>("android.sensor.info.maxFrameDuration", long.class);
             public static final Key<float[]> PHYSICAL_SIZE =
                     new Key<float[]>("android.sensor.info.physicalSize", float[].class);
-            public static final Key<int[]> PIXEL_ARRAY_SIZE =
-                    new Key<int[]>("android.sensor.info.pixelArraySize", int[].class);
-            public static final Key<Integer> WHITE_LEVEL =
-                    new Key<Integer>("android.sensor.info.whiteLevel", int.class);
         }
-
         public static final Key<Rational> BASE_GAIN_FACTOR =
                 new Key<Rational>("android.sensor.baseGainFactor", Rational.class);
-        public static final Key<int[]> BLACK_LEVEL_PATTERN =
-                new Key<int[]>("android.sensor.blackLevelPattern", int[].class);
-        public static final Key<Rational[]> CALIBRATION_TRANSFORM1 =
-                new Key<Rational[]>("android.sensor.calibrationTransform1", Rational[].class);
-        public static final Key<Rational[]> CALIBRATION_TRANSFORM2 =
-                new Key<Rational[]>("android.sensor.calibrationTransform2", Rational[].class);
-        public static final Key<Rational[]> COLOR_TRANSFORM1 =
-                new Key<Rational[]>("android.sensor.colorTransform1", Rational[].class);
-        public static final Key<Rational[]> COLOR_TRANSFORM2 =
-                new Key<Rational[]>("android.sensor.colorTransform2", Rational[].class);
-        public static final Key<Rational[]> FORWARD_MATRIX1 =
-                new Key<Rational[]>("android.sensor.forwardMatrix1", Rational[].class);
-        public static final Key<Rational[]> FORWARD_MATRIX2 =
-                new Key<Rational[]>("android.sensor.forwardMatrix2", Rational[].class);
         public static final Key<Integer> MAX_ANALOG_SENSITIVITY =
                 new Key<Integer>("android.sensor.maxAnalogSensitivity", int.class);
-        public static final Key<float[]> NOISE_MODEL_COEFFICIENTS =
-                new Key<float[]>("android.sensor.noiseModelCoefficients", float[].class);
         public static final Key<Integer> ORIENTATION =
                 new Key<Integer>("android.sensor.orientation", int.class);
 
-            public static final class ReferenceIlluminant1Key extends Key<Sensor.ReferenceIlluminant1Key.Enum> {
-                public enum Enum {
-                    DAYLIGHT,
-                    FLUORESCENT,
-                    TUNGSTEN,
-                    FLASH,
-                    FINE_WEATHER,
-                    CLOUDY_WEATHER,
-                    SHADE,
-                    DAYLIGHT_FLUORESCENT,
-                    DAY_WHITE_FLUORESCENT,
-                    COOL_WHITE_FLUORESCENT,
-                    WHITE_FLUORESCENT,
-                    STANDARD_A,
-                    STANDARD_B,
-                    STANDARD_C,
-                    D55,
-                    D65,
-                    D75,
-                    D50,
-                    ISO_STUDIO_TUNGSTEN;
-                }
-
-                public static final Enum DAYLIGHT = Enum.DAYLIGHT;
-                public static final Enum FLUORESCENT = Enum.FLUORESCENT;
-                public static final Enum TUNGSTEN = Enum.TUNGSTEN;
-                public static final Enum FLASH = Enum.FLASH;
-                public static final Enum FINE_WEATHER = Enum.FINE_WEATHER;
-                public static final Enum CLOUDY_WEATHER = Enum.CLOUDY_WEATHER;
-                public static final Enum SHADE = Enum.SHADE;
-                public static final Enum DAYLIGHT_FLUORESCENT = Enum.DAYLIGHT_FLUORESCENT;
-                public static final Enum DAY_WHITE_FLUORESCENT = Enum.DAY_WHITE_FLUORESCENT;
-                public static final Enum COOL_WHITE_FLUORESCENT = Enum.COOL_WHITE_FLUORESCENT;
-                public static final Enum WHITE_FLUORESCENT = Enum.WHITE_FLUORESCENT;
-                public static final Enum STANDARD_A = Enum.STANDARD_A;
-                public static final Enum STANDARD_B = Enum.STANDARD_B;
-                public static final Enum STANDARD_C = Enum.STANDARD_C;
-                public static final Enum D55 = Enum.D55;
-                public static final Enum D65 = Enum.D65;
-                public static final Enum D75 = Enum.D75;
-                public static final Enum D50 = Enum.D50;
-                public static final Enum ISO_STUDIO_TUNGSTEN = Enum.ISO_STUDIO_TUNGSTEN;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private ReferenceIlluminant1Key(String name) {
-                    super(name, Sensor.ReferenceIlluminant1Key.Enum.class);
-                }
-
-                static {
-                    CameraMetadata.registerEnumValues(Sensor.ReferenceIlluminant1Key.Enum.class, new int[] {
-                        1,  // DAYLIGHT
-                        2,  // FLUORESCENT
-                        3,  // TUNGSTEN
-                        4,  // FLASH
-                        9,  // FINE_WEATHER
-                        10,  // CLOUDY_WEATHER
-                        11,  // SHADE
-                        12,  // DAYLIGHT_FLUORESCENT
-                        13,  // DAY_WHITE_FLUORESCENT
-                        14,  // COOL_WHITE_FLUORESCENT
-                        15,  // WHITE_FLUORESCENT
-                        17,  // STANDARD_A
-                        18,  // STANDARD_B
-                        19,  // STANDARD_C
-                        20,  // D55
-                        21,  // D65
-                        22,  // D75
-                        23,  // D50
-                        24  // ISO_STUDIO_TUNGSTEN
-                    });
-                }
-            }
-
-        public static final Key<Sensor.ReferenceIlluminant1Key.Enum> REFERENCE_ILLUMINANT1 =
-                new ReferenceIlluminant1Key("android.sensor.referenceIlluminant1");
-        public static final Key<Byte> REFERENCE_ILLUMINANT2 =
-                new Key<Byte>("android.sensor.referenceIlluminant2", byte.class);
-
     }
+
     public static final class Statistics {
         public static final class Info {
             public static final Key<byte[]> AVAILABLE_FACE_DETECT_MODES =
                     new Key<byte[]>("android.statistics.info.availableFaceDetectModes", byte[].class);
-            public static final Key<Integer> HISTOGRAM_BUCKET_COUNT =
-                    new Key<Integer>("android.statistics.info.histogramBucketCount", int.class);
             public static final Key<Integer> MAX_FACE_COUNT =
                     new Key<Integer>("android.statistics.info.maxFaceCount", int.class);
-            public static final Key<Integer> MAX_HISTOGRAM_COUNT =
-                    new Key<Integer>("android.statistics.info.maxHistogramCount", int.class);
-            public static final Key<Integer> MAX_SHARPNESS_MAP_VALUE =
-                    new Key<Integer>("android.statistics.info.maxSharpnessMapValue", int.class);
-            public static final Key<int[]> SHARPNESS_MAP_SIZE =
-                    new Key<int[]>("android.statistics.info.sharpnessMapSize", int[].class);
         }
 
-
     }
-    public static final class Tonemap {
 
+    public static final class Tonemap {
         public static final Key<Integer> MAX_CURVE_POINTS =
                 new Key<Integer>("android.tonemap.maxCurvePoints", int.class);
 
     }
+
+    /**
+     * @hide
+     */
     public static final class Led {
 
-
+            /**
+             * @hide
+             */
             public static final class AvailableLedsKey extends Key<Led.AvailableLedsKey.Enum[]> {
                 public enum Enum {
                     TRANSMIT;
@@ -404,12 +232,15 @@
 
             }
 
+        /**
+         * @hide
+         */
         public static final Key<Led.AvailableLedsKey.Enum[]> AVAILABLE_LEDS =
                 new AvailableLedsKey("android.led.availableLeds");
 
     }
-    public static final class Info {
 
+    public static final class Info {
 
             public static final class SupportedHardwareLevelKey extends Key<Info.SupportedHardwareLevelKey.Enum> {
                 public enum Enum {
@@ -431,6 +262,7 @@
                 new SupportedHardwareLevelKey("android.info.supportedHardwareLevel");
 
     }
+
 }
 
 
diff --git a/core/java/android/hardware/photography/CaptureRequestKeys.java b/core/java/android/hardware/photography/CaptureRequestKeys.java
index b8abe2b..b23e71d 100644
--- a/core/java/android/hardware/photography/CaptureRequestKeys.java
+++ b/core/java/android/hardware/photography/CaptureRequestKeys.java
@@ -40,7 +40,6 @@
 public final class CaptureRequestKeys {
     public static final class ColorCorrection {
 
-
             public static final class ModeKey extends Key<ColorCorrection.ModeKey.Enum> {
                 public enum Enum {
                     TRANSFORM_MATRIX,
@@ -65,8 +64,8 @@
                 new Key<float[]>("android.colorCorrection.transform", float[].class);
 
     }
-    public static final class Control {
 
+    public static final class Control {
 
             public static final class AeAntibandingModeKey extends Key<Control.AeAntibandingModeKey.Enum> {
                 public enum Enum {
@@ -435,32 +434,9 @@
                 new VideoStabilizationModeKey("android.control.videoStabilizationMode");
 
     }
-    public static final class Demosaic {
 
-
-            public static final class ModeKey extends Key<Demosaic.ModeKey.Enum> {
-                public enum Enum {
-                    FAST,
-                    HIGH_QUALITY;
-                }
-
-                public static final Enum FAST = Enum.FAST;
-                public static final Enum HIGH_QUALITY = Enum.HIGH_QUALITY;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private ModeKey(String name) {
-                    super(name, Demosaic.ModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<Demosaic.ModeKey.Enum> MODE =
-                new ModeKey("android.demosaic.mode");
-
-    }
     public static final class Edge {
 
-
             public static final class ModeKey extends Key<Edge.ModeKey.Enum> {
                 public enum Enum {
                     OFF,
@@ -481,16 +457,10 @@
 
         public static final Key<Edge.ModeKey.Enum> MODE =
                 new ModeKey("android.edge.mode");
-        public static final Key<Byte> STRENGTH =
-                new Key<Byte>("android.edge.strength", byte.class);
 
     }
-    public static final class Flash {
 
-        public static final Key<Byte> FIRING_POWER =
-                new Key<Byte>("android.flash.firingPower", byte.class);
-        public static final Key<Long> FIRING_TIME =
-                new Key<Long>("android.flash.firingTime", long.class);
+    public static final class Flash {
 
             public static final class ModeKey extends Key<Flash.ModeKey.Enum> {
                 public enum Enum {
@@ -514,60 +484,8 @@
                 new ModeKey("android.flash.mode");
 
     }
-    public static final class Geometric {
 
-
-            public static final class ModeKey extends Key<Geometric.ModeKey.Enum> {
-                public enum Enum {
-                    OFF,
-                    FAST,
-                    HIGH_QUALITY;
-                }
-
-                public static final Enum OFF = Enum.OFF;
-                public static final Enum FAST = Enum.FAST;
-                public static final Enum HIGH_QUALITY = Enum.HIGH_QUALITY;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private ModeKey(String name) {
-                    super(name, Geometric.ModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<Geometric.ModeKey.Enum> MODE =
-                new ModeKey("android.geometric.mode");
-        public static final Key<Byte> STRENGTH =
-                new Key<Byte>("android.geometric.strength", byte.class);
-
-    }
-    public static final class HotPixel {
-
-
-            public static final class ModeKey extends Key<HotPixel.ModeKey.Enum> {
-                public enum Enum {
-                    OFF,
-                    FAST,
-                    HIGH_QUALITY;
-                }
-
-                public static final Enum OFF = Enum.OFF;
-                public static final Enum FAST = Enum.FAST;
-                public static final Enum HIGH_QUALITY = Enum.HIGH_QUALITY;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private ModeKey(String name) {
-                    super(name, HotPixel.ModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<HotPixel.ModeKey.Enum> MODE =
-                new ModeKey("android.hotPixel.mode");
-
-    }
     public static final class Jpeg {
-
         public static final Key<double[]> GPS_COORDINATES =
                 new Key<double[]>("android.jpeg.gpsCoordinates", double[].class);
         public static final Key<Byte> GPS_PROCESSING_METHOD =
@@ -584,8 +502,8 @@
                 new Key<int[]>("android.jpeg.thumbnailSize", int[].class);
 
     }
-    public static final class Lens {
 
+    public static final class Lens {
         public static final Key<Float> APERTURE =
                 new Key<Float>("android.lens.aperture", float.class);
         public static final Key<Float> FILTER_DENSITY =
@@ -615,8 +533,8 @@
                 new OpticalStabilizationModeKey("android.lens.opticalStabilizationMode");
 
     }
-    public static final class NoiseReduction {
 
+    public static final class NoiseReduction {
 
             public static final class ModeKey extends Key<NoiseReduction.ModeKey.Enum> {
                 public enum Enum {
@@ -638,68 +556,28 @@
 
         public static final Key<NoiseReduction.ModeKey.Enum> MODE =
                 new ModeKey("android.noiseReduction.mode");
-        public static final Key<Byte> STRENGTH =
-                new Key<Byte>("android.noiseReduction.strength", byte.class);
 
     }
-    public static final class Request {
 
-        public static final Key<Integer> FRAME_COUNT =
-                new Key<Integer>("android.request.frameCount", int.class);
+    /**
+     * @hide
+     */
+    public static final class Request {
+        /**
+         * @hide
+         */
         public static final Key<Integer> ID =
                 new Key<Integer>("android.request.id", int.class);
-        public static final Key<Byte> INPUT_STREAMS =
-                new Key<Byte>("android.request.inputStreams", byte.class);
-
-            public static final class MetadataModeKey extends Key<Request.MetadataModeKey.Enum> {
-                public enum Enum {
-                    NONE,
-                    FULL;
-                }
-
-                public static final Enum NONE = Enum.NONE;
-                public static final Enum FULL = Enum.FULL;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private MetadataModeKey(String name) {
-                    super(name, Request.MetadataModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<Request.MetadataModeKey.Enum> METADATA_MODE =
-                new MetadataModeKey("android.request.metadataMode");
-        public static final Key<Byte> OUTPUT_STREAMS =
-                new Key<Byte>("android.request.outputStreams", byte.class);
-
-            public static final class TypeKey extends Key<Request.TypeKey.Enum> {
-                public enum Enum {
-                    CAPTURE,
-                    REPROCESS;
-                }
-
-                public static final Enum CAPTURE = Enum.CAPTURE;
-                public static final Enum REPROCESS = Enum.REPROCESS;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private TypeKey(String name) {
-                    super(name, Request.TypeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<Request.TypeKey.Enum> TYPE =
-                new TypeKey("android.request.type");
 
     }
-    public static final class Scaler {
 
+    public static final class Scaler {
         public static final Key<int[]> CROP_REGION =
                 new Key<int[]>("android.scaler.cropRegion", int[].class);
 
     }
-    public static final class Sensor {
 
+    public static final class Sensor {
         public static final Key<Long> EXPOSURE_TIME =
                 new Key<Long>("android.sensor.exposureTime", long.class);
         public static final Key<Long> FRAME_DURATION =
@@ -708,36 +586,9 @@
                 new Key<Integer>("android.sensor.sensitivity", int.class);
 
     }
-    public static final class Shading {
 
-
-            public static final class ModeKey extends Key<Shading.ModeKey.Enum> {
-                public enum Enum {
-                    OFF,
-                    FAST,
-                    HIGH_QUALITY;
-                }
-
-                public static final Enum OFF = Enum.OFF;
-                public static final Enum FAST = Enum.FAST;
-                public static final Enum HIGH_QUALITY = Enum.HIGH_QUALITY;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private ModeKey(String name) {
-                    super(name, Shading.ModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<Shading.ModeKey.Enum> MODE =
-                new ModeKey("android.shading.mode");
-        public static final Key<Byte> STRENGTH =
-                new Key<Byte>("android.shading.strength", byte.class);
-
-    }
     public static final class Statistics {
 
-
             public static final class FaceDetectModeKey extends Key<Statistics.FaceDetectModeKey.Enum> {
                 public enum Enum {
                     OFF,
@@ -759,47 +610,9 @@
         public static final Key<Statistics.FaceDetectModeKey.Enum> FACE_DETECT_MODE =
                 new FaceDetectModeKey("android.statistics.faceDetectMode");
 
-            public static final class HistogramModeKey extends Key<Statistics.HistogramModeKey.Enum> {
-                public enum Enum {
-                    OFF,
-                    ON;
-                }
-
-                public static final Enum OFF = Enum.OFF;
-                public static final Enum ON = Enum.ON;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private HistogramModeKey(String name) {
-                    super(name, Statistics.HistogramModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<Statistics.HistogramModeKey.Enum> HISTOGRAM_MODE =
-                new HistogramModeKey("android.statistics.histogramMode");
-
-            public static final class SharpnessMapModeKey extends Key<Statistics.SharpnessMapModeKey.Enum> {
-                public enum Enum {
-                    OFF,
-                    ON;
-                }
-
-                public static final Enum OFF = Enum.OFF;
-                public static final Enum ON = Enum.ON;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private SharpnessMapModeKey(String name) {
-                    super(name, Statistics.SharpnessMapModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<Statistics.SharpnessMapModeKey.Enum> SHARPNESS_MAP_MODE =
-                new SharpnessMapModeKey("android.statistics.sharpnessMapMode");
-
     }
-    public static final class Tonemap {
 
+    public static final class Tonemap {
         public static final Key<Float> CURVE_BLUE =
                 new Key<Float>("android.tonemap.curveBlue", float.class);
         public static final Key<Float> CURVE_GREEN =
@@ -829,9 +642,15 @@
                 new ModeKey("android.tonemap.mode");
 
     }
+
+    /**
+     * @hide
+     */
     public static final class Led {
 
-
+            /**
+             * @hide
+             */
             public static final class TransmitKey extends Key<Led.TransmitKey.Enum> {
                 public enum Enum {
                     OFF,
@@ -848,10 +667,14 @@
 
             }
 
+        /**
+         * @hide
+         */
         public static final Key<Led.TransmitKey.Enum> TRANSMIT =
                 new TransmitKey("android.led.transmit");
 
     }
+
 }
 
 
diff --git a/core/java/android/hardware/photography/CaptureResultKeys.java b/core/java/android/hardware/photography/CaptureResultKeys.java
index 5a638ed..e44fc91 100644
--- a/core/java/android/hardware/photography/CaptureResultKeys.java
+++ b/core/java/android/hardware/photography/CaptureResultKeys.java
@@ -40,7 +40,6 @@
 public final class CaptureResultKeys {
     public static final class ColorCorrection {
 
-
             public static final class ModeKey extends Key<ColorCorrection.ModeKey.Enum> {
                 public enum Enum {
                     TRANSFORM_MATRIX,
@@ -63,8 +62,11 @@
                 new ModeKey("android.colorCorrection.mode");
 
     }
-    public static final class Control {
 
+    public static final class Control {
+        /**
+         * @hide
+         */
         public static final Key<Integer> AE_PRECAPTURE_ID =
                 new Key<Integer>("android.control.aePrecaptureId", int.class);
         public static final Key<int[]> AE_REGIONS =
@@ -152,6 +154,9 @@
 
         public static final Key<Control.AfStateKey.Enum> AF_STATE =
                 new AfStateKey("android.control.afState");
+        /**
+         * @hide
+         */
         public static final Key<Integer> AF_TRIGGER_ID =
                 new Key<Integer>("android.control.afTriggerId", int.class);
 
@@ -235,8 +240,8 @@
                 new ModeKey("android.control.mode");
 
     }
-    public static final class Edge {
 
+    public static final class Edge {
 
             public static final class ModeKey extends Key<Edge.ModeKey.Enum> {
                 public enum Enum {
@@ -260,12 +265,8 @@
                 new ModeKey("android.edge.mode");
 
     }
-    public static final class Flash {
 
-        public static final Key<Byte> FIRING_POWER =
-                new Key<Byte>("android.flash.firingPower", byte.class);
-        public static final Key<Long> FIRING_TIME =
-                new Key<Long>("android.flash.firingTime", long.class);
+    public static final class Flash {
 
             public static final class ModeKey extends Key<Flash.ModeKey.Enum> {
                 public enum Enum {
@@ -312,33 +313,8 @@
                 new StateKey("android.flash.state");
 
     }
-    public static final class HotPixel {
 
-
-            public static final class ModeKey extends Key<HotPixel.ModeKey.Enum> {
-                public enum Enum {
-                    OFF,
-                    FAST,
-                    HIGH_QUALITY;
-                }
-
-                public static final Enum OFF = Enum.OFF;
-                public static final Enum FAST = Enum.FAST;
-                public static final Enum HIGH_QUALITY = Enum.HIGH_QUALITY;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private ModeKey(String name) {
-                    super(name, HotPixel.ModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<HotPixel.ModeKey.Enum> MODE =
-                new ModeKey("android.hotPixel.mode");
-
-    }
     public static final class Jpeg {
-
         public static final Key<double[]> GPS_COORDINATES =
                 new Key<double[]>("android.jpeg.gpsCoordinates", double[].class);
         public static final Key<Byte> GPS_PROCESSING_METHOD =
@@ -349,16 +325,14 @@
                 new Key<Integer>("android.jpeg.orientation", int.class);
         public static final Key<Byte> QUALITY =
                 new Key<Byte>("android.jpeg.quality", byte.class);
-        public static final Key<Integer> SIZE =
-                new Key<Integer>("android.jpeg.size", int.class);
         public static final Key<Byte> THUMBNAIL_QUALITY =
                 new Key<Byte>("android.jpeg.thumbnailQuality", byte.class);
         public static final Key<int[]> THUMBNAIL_SIZE =
                 new Key<int[]>("android.jpeg.thumbnailSize", int[].class);
 
     }
-    public static final class Lens {
 
+    public static final class Lens {
         public static final Key<Float> APERTURE =
                 new Key<Float>("android.lens.aperture", float.class);
         public static final Key<Float> FILTER_DENSITY =
@@ -407,8 +381,8 @@
                 new StateKey("android.lens.state");
 
     }
-    public static final class NoiseReduction {
 
+    public static final class NoiseReduction {
 
             public static final class ModeKey extends Key<NoiseReduction.ModeKey.Enum> {
                 public enum Enum {
@@ -432,43 +406,25 @@
                 new ModeKey("android.noiseReduction.mode");
 
     }
-    public static final class Request {
 
+    public static final class Request {
         public static final Key<Integer> FRAME_COUNT =
                 new Key<Integer>("android.request.frameCount", int.class);
+        /**
+         * @hide
+         */
         public static final Key<Integer> ID =
                 new Key<Integer>("android.request.id", int.class);
 
-            public static final class MetadataModeKey extends Key<Request.MetadataModeKey.Enum> {
-                public enum Enum {
-                    NONE,
-                    FULL;
-                }
-
-                public static final Enum NONE = Enum.NONE;
-                public static final Enum FULL = Enum.FULL;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private MetadataModeKey(String name) {
-                    super(name, Request.MetadataModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<Request.MetadataModeKey.Enum> METADATA_MODE =
-                new MetadataModeKey("android.request.metadataMode");
-        public static final Key<Byte> OUTPUT_STREAMS =
-                new Key<Byte>("android.request.outputStreams", byte.class);
-
     }
-    public static final class Scaler {
 
+    public static final class Scaler {
         public static final Key<int[]> CROP_REGION =
                 new Key<int[]>("android.scaler.cropRegion", int[].class);
 
     }
-    public static final class Sensor {
 
+    public static final class Sensor {
         public static final Key<Long> EXPOSURE_TIME =
                 new Key<Long>("android.sensor.exposureTime", long.class);
         public static final Key<Long> FRAME_DURATION =
@@ -479,34 +435,9 @@
                 new Key<Long>("android.sensor.timestamp", long.class);
 
     }
-    public static final class Shading {
 
-
-            public static final class ModeKey extends Key<Shading.ModeKey.Enum> {
-                public enum Enum {
-                    OFF,
-                    FAST,
-                    HIGH_QUALITY;
-                }
-
-                public static final Enum OFF = Enum.OFF;
-                public static final Enum FAST = Enum.FAST;
-                public static final Enum HIGH_QUALITY = Enum.HIGH_QUALITY;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private ModeKey(String name) {
-                    super(name, Shading.ModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<Shading.ModeKey.Enum> MODE =
-                new ModeKey("android.shading.mode");
-
-    }
     public static final class Statistics {
 
-
             public static final class FaceDetectModeKey extends Key<Statistics.FaceDetectModeKey.Enum> {
                 public enum Enum {
                     OFF,
@@ -535,52 +466,10 @@
                 new Key<int[]>("android.statistics.faceRectangles", int[].class);
         public static final Key<byte[]> FACE_SCORES =
                 new Key<byte[]>("android.statistics.faceScores", byte[].class);
-        public static final Key<int[]> HISTOGRAM =
-                new Key<int[]>("android.statistics.histogram", int[].class);
-
-            public static final class HistogramModeKey extends Key<Statistics.HistogramModeKey.Enum> {
-                public enum Enum {
-                    OFF,
-                    ON;
-                }
-
-                public static final Enum OFF = Enum.OFF;
-                public static final Enum ON = Enum.ON;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private HistogramModeKey(String name) {
-                    super(name, Statistics.HistogramModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<Statistics.HistogramModeKey.Enum> HISTOGRAM_MODE =
-                new HistogramModeKey("android.statistics.histogramMode");
-        public static final Key<int[]> SHARPNESS_MAP =
-                new Key<int[]>("android.statistics.sharpnessMap", int[].class);
-
-            public static final class SharpnessMapModeKey extends Key<Statistics.SharpnessMapModeKey.Enum> {
-                public enum Enum {
-                    OFF,
-                    ON;
-                }
-
-                public static final Enum OFF = Enum.OFF;
-                public static final Enum ON = Enum.ON;
-
-                // TODO: remove requirement for constructor by making Key an interface
-                private SharpnessMapModeKey(String name) {
-                    super(name, Statistics.SharpnessMapModeKey.Enum.class);
-                }
-
-            }
-
-        public static final Key<Statistics.SharpnessMapModeKey.Enum> SHARPNESS_MAP_MODE =
-                new SharpnessMapModeKey("android.statistics.sharpnessMapMode");
 
     }
-    public static final class Tonemap {
 
+    public static final class Tonemap {
         public static final Key<Float> CURVE_BLUE =
                 new Key<Float>("android.tonemap.curveBlue", float.class);
         public static final Key<Float> CURVE_GREEN =
@@ -610,9 +499,15 @@
                 new ModeKey("android.tonemap.mode");
 
     }
+
+    /**
+     * @hide
+     */
     public static final class Led {
 
-
+            /**
+             * @hide
+             */
             public static final class TransmitKey extends Key<Led.TransmitKey.Enum> {
                 public enum Enum {
                     OFF,
@@ -629,10 +524,14 @@
 
             }
 
+        /**
+         * @hide
+         */
         public static final Key<Led.TransmitKey.Enum> TRANSMIT =
                 new TransmitKey("android.led.transmit");
 
     }
+
 }
 
 
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index c5f473e..0a6db25 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -192,6 +192,14 @@
             return dalvikPss + nativePss + otherPss;
         }
 
+        /**
+         * @hide Return total PSS memory usage in kB.
+         */
+        public int getTotalUss() {
+            return dalvikPrivateClean + dalvikPrivateDirty
+                    + nativePrivateClean + nativePrivateDirty
+                    + otherPrivateClean + otherPrivateDirty;
+        }
 
         /**
          * Return total PSS memory usage in kB.
@@ -1001,9 +1009,10 @@
 
     /**
      * Retrieves the PSS memory used by the process as given by the
-     * smaps. @hide
+     * smaps.  Optionally supply a long array of 1 entry to also
+     * receive the uss of the process.  @hide
      */
-    public static native long getPss(int pid);
+    public static native long getPss(int pid, long[] outUss);
 
     /**
      * Establish an object allocation limit in the current thread.
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 159d3eb..1d482dc 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -894,6 +894,19 @@
     public static final native boolean setOomAdj(int pid, int amt);
 
     /**
+     * Adjust the swappiness level for a process.
+     *
+     * @param pid The process identifier to set.
+     * @param is_increased Whether swappiness should be increased or default.
+     *
+     * @return Returns true if the underlying system supports this
+     *         feature, else false.
+     *
+     * {@hide}
+     */
+    public static final native boolean setSwappiness(int pid, boolean is_increased);
+
+    /**
      * Change this process's argv[0] parameter.  This can be useful to show
      * more descriptive information in things like the 'ps' command.
      * 
diff --git a/core/java/android/print/FileDocumentAdapter.java b/core/java/android/print/FileDocumentAdapter.java
new file mode 100644
index 0000000..2871d45
--- /dev/null
+++ b/core/java/android/print/FileDocumentAdapter.java
@@ -0,0 +1,140 @@
+/*
+ * 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 android.print;
+
+import android.content.Context;
+import android.os.AsyncTask;
+import android.os.CancellationSignal;
+import android.os.CancellationSignal.OnCancelListener;
+import android.util.Log;
+
+import com.android.internal.R;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Adapter for printing files.
+ */
+final class FileDocumentAdapter extends PrintDocumentAdapter {
+
+    private static final String LOG_TAG = "FileDocumentAdapter";
+
+    private final Context mContext;
+
+    private final File mFile;
+
+    private WriteFileAsyncTask mWriteFileAsyncTask;
+
+    public FileDocumentAdapter(Context context, File file) {
+        if (file == null) {
+            throw new IllegalArgumentException("File cannot be null!");
+        }
+        mContext = context;
+        mFile = file;
+    }
+
+    @Override
+    public void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+            CancellationSignal cancellationSignal, LayoutResultCallback callback) {
+        // TODO: When we have a PDF rendering library we should query the page count.
+        PrintDocumentInfo info =  new PrintDocumentInfo.Builder()
+        .setPageCount(PrintDocumentInfo.PAGE_COUNT_UNKNOWN).create();
+        callback.onLayoutFinished(info, false);
+    }
+
+    @Override
+    public void onWrite(List<PageRange> pages, FileDescriptor destination,
+            CancellationSignal cancellationSignal, WriteResultCallback callback) {
+        mWriteFileAsyncTask = new WriteFileAsyncTask(destination, cancellationSignal, callback);
+        mWriteFileAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
+                (Void[]) null);
+    }
+
+    private final class WriteFileAsyncTask extends AsyncTask<Void, Void, Void> {
+
+        private final FileDescriptor mDestination;
+
+        private final WriteResultCallback mResultCallback;
+
+        private final CancellationSignal mCancellationSignal;
+
+        public WriteFileAsyncTask(FileDescriptor destination,
+                CancellationSignal cancellationSignal, WriteResultCallback callback) {
+            mDestination = destination;
+            mResultCallback = callback;
+            mCancellationSignal = cancellationSignal;
+            mCancellationSignal.setOnCancelListener(new OnCancelListener() {
+                @Override
+                public void onCancel() {
+                    cancel(true);
+                }
+            });
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            InputStream in = null;
+            OutputStream out = new FileOutputStream(mDestination);
+            final byte[] buffer = new byte[8192];
+            try {
+                in = new FileInputStream(mFile);
+                while (true) {
+                    if (isCancelled()) {
+                        break;
+                    }
+                    final int readByteCount = in.read(buffer);
+                    if (readByteCount < 0) {
+                        break;
+                    }
+                    out.write(buffer, 0, readByteCount);
+                }
+             } catch (IOException ioe) {
+                 Log.e(LOG_TAG, "Error writing data!", ioe);
+                 mResultCallback.onWriteFailed(mContext.getString(
+                         R.string.write_fail_reason_cannot_write));
+             } finally {
+                IoUtils.closeQuietly(in);
+                IoUtils.closeQuietly(out);
+            }
+            return null;
+        }
+
+        @Override
+        protected void onPostExecute(Void result) {
+            List<PageRange> pages = new ArrayList<PageRange>();
+            pages.add(PageRange.ALL_PAGES);
+            mResultCallback.onWriteFinished(pages);
+        }
+
+        @Override
+        protected void onCancelled(Void result) {
+            mResultCallback.onWriteFailed(mContext.getString(
+                    R.string.write_fail_reason_cancelled));
+        }
+    }
+}
+
diff --git a/core/java/android/print/IPrintResultCallback.aidl b/core/java/android/print/ILayoutResultCallback.aidl
similarity index 64%
copy from core/java/android/print/IPrintResultCallback.aidl
copy to core/java/android/print/ILayoutResultCallback.aidl
index 838377e..e4d79f3 100644
--- a/core/java/android/print/IPrintResultCallback.aidl
+++ b/core/java/android/print/ILayoutResultCallback.aidl
@@ -17,17 +17,15 @@
 package android.print;
 
 import android.os.ICancellationSignal;
-import android.print.PageRange;
-import android.print.PrintAdapterInfo;
+import android.print.PrintDocumentInfo;
 
 /**
- * Callbacks for observing the print progress (writing of printed content)
- * of a PrintAdapter.
+ * Callback for observing the result of android.print.PrintAdapter#onLayout.
  *
  * @hide
  */
-oneway interface IPrintResultCallback {
-    void onPrintStarted(in PrintAdapterInfo info, ICancellationSignal cancellationSignal);
-    void onPrintFinished(in List<PageRange> pages);
-    void onPrintFailed(CharSequence error);
+oneway interface ILayoutResultCallback {
+    void onLayoutStarted(ICancellationSignal cancellationSignal);
+    void onLayoutFinished(in PrintDocumentInfo info, boolean changed);
+    void onLayoutFailed(CharSequence error);
 }
diff --git a/core/java/android/print/IPrintAdapter.aidl b/core/java/android/print/IPrintDocumentAdapter.aidl
similarity index 70%
rename from core/java/android/print/IPrintAdapter.aidl
rename to core/java/android/print/IPrintDocumentAdapter.aidl
index f3ff8c4..36938e3 100644
--- a/core/java/android/print/IPrintAdapter.aidl
+++ b/core/java/android/print/IPrintDocumentAdapter.aidl
@@ -17,7 +17,8 @@
 package android.print;
 
 import android.os.ParcelFileDescriptor;
-import android.print.IPrintResultCallback;
+import android.print.ILayoutResultCallback;
+import android.print.IWriteResultCallback;
 import android.print.PageRange;
 import android.print.PrintAttributes;
 
@@ -26,10 +27,11 @@
  *
  * @hide
  */
-oneway interface IPrintAdapter {
+oneway interface IPrintDocumentAdapter {
     void start();
-    void printAttributesChanged(in PrintAttributes attributes);
-    void print(in List<PageRange> pages, in ParcelFileDescriptor fd,
-            IPrintResultCallback callback);
+    void layout(in PrintAttributes oldAttributes, in PrintAttributes newAttributes,
+            ILayoutResultCallback callback);
+    void write(in List<PageRange> pages, in ParcelFileDescriptor fd,
+            IWriteResultCallback callback);
     void finish();
 }
diff --git a/core/java/android/print/IPrintManager.aidl b/core/java/android/print/IPrintManager.aidl
index ff9877e..a466e74 100644
--- a/core/java/android/print/IPrintManager.aidl
+++ b/core/java/android/print/IPrintManager.aidl
@@ -16,11 +16,8 @@
 
 package android.print;
 
-import android.os.ICancellationSignal;
-import android.print.IPrintAdapter;
+import android.print.IPrintDocumentAdapter;
 import android.print.IPrintClient;
-import android.print.IPrinterDiscoveryObserver;
-import android.print.PrinterId;
 import android.print.PrintJobInfo;
 import android.print.PrintAttributes;
 
@@ -30,12 +27,11 @@
  * @hide
  */
 interface IPrintManager {
-    List<PrintJobInfo> getPrintJobs(int appId, int userId);
-    PrintJobInfo getPrintJob(int printJobId, int appId, int userId);
-    PrintJobInfo print(String printJobName, in IPrintClient client, in IPrintAdapter printAdapter,
-            in PrintAttributes attributes, int appId, int userId);
+    List<PrintJobInfo> getPrintJobInfos(int appId, int userId);
+    PrintJobInfo getPrintJobInfo(int printJobId, int appId, int userId);
+    PrintJobInfo print(String printJobName, in IPrintClient client,
+            in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes,
+            int appId, int userId);
     void cancelPrintJob(int printJobId, int appId, int userId);
-    void onPrintJobQueued(in PrinterId printerId, in PrintJobInfo printJob);
-    void startDiscoverPrinters(IPrinterDiscoveryObserver observer);
-    void stopDiscoverPrinters();
+
 }
diff --git a/core/java/android/print/IPrintSpoolerService.aidl b/core/java/android/print/IPrintSpooler.aidl
similarity index 64%
rename from core/java/android/print/IPrintSpoolerService.aidl
rename to core/java/android/print/IPrintSpooler.aidl
index e84d592..c55205d 100644
--- a/core/java/android/print/IPrintSpoolerService.aidl
+++ b/core/java/android/print/IPrintSpooler.aidl
@@ -18,32 +18,35 @@
 
 import android.content.ComponentName;
 import android.os.ParcelFileDescriptor;
-import android.print.IPrintAdapter;
+import android.print.IPrintDocumentAdapter;
 import android.print.IPrintClient;
-import android.print.IPrintSpoolerServiceCallbacks;
+import android.print.IPrintSpoolerClient;
+import android.print.IPrintSpoolerCallbacks;
 import android.print.PrinterInfo;
 import android.print.PrintAttributes;
 
 /**
  * Interface for communication with the print spooler service.
  *
- * @see android.print.IPrintSpoolerServiceCallbacks
+ * @see android.print.IPrintSpoolerCallbacks
  *
  * @hide
  */
-oneway interface IPrintSpoolerService {
-    void getPrintJobs(IPrintSpoolerServiceCallbacks callback, in ComponentName componentName,
+oneway interface IPrintSpooler {
+    void getPrintJobInfos(IPrintSpoolerCallbacks callback, in ComponentName componentName,
             int state, int appId, int sequence);
-    void getPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+    void getPrintJobInfo(int printJobId, IPrintSpoolerCallbacks callback,
             int appId, int sequence);
-    void createPrintJob(String printJobName, in IPrintClient client, in IPrintAdapter printAdapter,
-            in PrintAttributes attributes, IPrintSpoolerServiceCallbacks callback, int appId,
-            int sequence);
-    void cancelPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+    void createPrintJob(String printJobName, in IPrintClient client,
+            in IPrintDocumentAdapter printAdapter, in PrintAttributes attributes,
+            IPrintSpoolerCallbacks callback, int appId, int sequence);
+    void cancelPrintJob(int printJobId, IPrintSpoolerCallbacks callback,
             int appId, int sequence);
-    void setPrintJobState(int printJobId, int status, IPrintSpoolerServiceCallbacks callback,
+    void setPrintJobState(int printJobId, int status, IPrintSpoolerCallbacks callback,
             int sequence);
-    void setPrintJobTag(int printJobId, String tag, IPrintSpoolerServiceCallbacks callback,
+    void setPrintJobTag(int printJobId, String tag, IPrintSpoolerCallbacks callback,
             int sequence);
     void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
-}
\ No newline at end of file
+    void setClient(IPrintSpoolerClient client);
+    void notifyClientForActivteJobs();
+}
diff --git a/core/java/android/print/IPrintSpoolerServiceCallbacks.aidl b/core/java/android/print/IPrintSpoolerCallbacks.aidl
similarity index 90%
rename from core/java/android/print/IPrintSpoolerServiceCallbacks.aidl
rename to core/java/android/print/IPrintSpoolerCallbacks.aidl
index 0c51913..7912964 100644
--- a/core/java/android/print/IPrintSpoolerServiceCallbacks.aidl
+++ b/core/java/android/print/IPrintSpoolerCallbacks.aidl
@@ -26,8 +26,8 @@
  *
  * @hide
  */
-oneway interface IPrintSpoolerServiceCallbacks {
-    void onGetPrintJobsResult(in List<PrintJobInfo> printJob, int sequence);
+oneway interface IPrintSpoolerCallbacks {
+    void onGetPrintJobInfosResult(in List<PrintJobInfo> printJob, int sequence);
     void onGetPrintJobInfoResult(in PrintJobInfo printJob, int sequence);
     void onCreatePrintJobResult(in PrintJobInfo printJob, int sequence);
     void onCancelPrintJobResult(boolean canceled, int sequence);
diff --git a/core/java/android/print/IPrintSpoolerClient.aidl b/core/java/android/print/IPrintSpoolerClient.aidl
new file mode 100644
index 0000000..47975e1
--- /dev/null
+++ b/core/java/android/print/IPrintSpoolerClient.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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 android.print;
+
+import android.content.ComponentName;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrintJobInfo;
+
+
+/**
+ * Interface for receiving interesting state updates from the print spooler.
+ *
+ * @hide
+ */
+oneway interface IPrintSpoolerClient {
+    void onPrintJobQueued(in PrintJobInfo printJob);
+    void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer);
+    void onStopPrinterDiscovery();
+    void onAllPrintJobsForServiceHandled(in ComponentName printService);
+    void onAllPrintJobsHandled();
+}
diff --git a/core/java/android/print/IPrintResultCallback.aidl b/core/java/android/print/IPrintSpoolerObserver.aidl
similarity index 60%
copy from core/java/android/print/IPrintResultCallback.aidl
copy to core/java/android/print/IPrintSpoolerObserver.aidl
index 838377e..7b8f40e 100644
--- a/core/java/android/print/IPrintResultCallback.aidl
+++ b/core/java/android/print/IPrintSpoolerObserver.aidl
@@ -16,18 +16,16 @@
 
 package android.print;
 
-import android.os.ICancellationSignal;
-import android.print.PageRange;
-import android.print.PrintAdapterInfo;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
 
 /**
- * Callbacks for observing the print progress (writing of printed content)
- * of a PrintAdapter.
+ * Interface for observing the state of the print spooler.
  *
  * @hide
  */
-oneway interface IPrintResultCallback {
-    void onPrintStarted(in PrintAdapterInfo info, ICancellationSignal cancellationSignal);
-    void onPrintFinished(in List<PageRange> pages);
-    void onPrintFailed(CharSequence error);
+oneway interface IPrinterDiscoveryObserver {
+    void onPrintJobQueued(in PrinterId printerId, in PrintJobInfo printJob);
+    void onAllPrintJobsHandled(in ComponentName printService);
+    void onAllPrintJobsHandled();
 }
diff --git a/core/java/android/print/IPrintResultCallback.aidl b/core/java/android/print/IWriteResultCallback.aidl
similarity index 67%
rename from core/java/android/print/IPrintResultCallback.aidl
rename to core/java/android/print/IWriteResultCallback.aidl
index 838377e..d5428b1 100644
--- a/core/java/android/print/IPrintResultCallback.aidl
+++ b/core/java/android/print/IWriteResultCallback.aidl
@@ -18,16 +18,14 @@
 
 import android.os.ICancellationSignal;
 import android.print.PageRange;
-import android.print.PrintAdapterInfo;
 
 /**
- * Callbacks for observing the print progress (writing of printed content)
- * of a PrintAdapter.
+ * Callback for observing the result of android.print.DocuemntAdapter#onWrite.
  *
  * @hide
  */
-oneway interface IPrintResultCallback {
-    void onPrintStarted(in PrintAdapterInfo info, ICancellationSignal cancellationSignal);
-    void onPrintFinished(in List<PageRange> pages);
-    void onPrintFailed(CharSequence error);
+oneway interface IWriteResultCallback {
+    void onWriteStarted(ICancellationSignal cancellationSignal);
+    void onWriteFinished(in List<PageRange> pages);
+    void onWriteFailed(CharSequence error);
 }
diff --git a/core/java/android/print/PrintAdapter.java b/core/java/android/print/PrintAdapter.java
deleted file mode 100644
index 6547c55..0000000
--- a/core/java/android/print/PrintAdapter.java
+++ /dev/null
@@ -1,164 +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 android.print;
-
-import android.os.CancellationSignal;
-
-import java.io.FileDescriptor;
-import java.util.List;
-
-/**
- * Base class that provides data to be printed.
- *
- * <h3>Lifecycle</h3>
- * <p>
- * <ul>
- * <li>
- * You will receive a call on {@link #onStart()} when printing starts.
- * This callback can be used to allocate resources.
- * </li>
- * <li>
- * Next you will get one or more calls to {@link #onPrintAttributesChanged(
- * PrintAttributes) to informs you that the print attributes (page size, density,
- * etc) changed giving you an opportunity to re-layout the content.
- * </li>
- * <li>
- * After every {@link #onPrintAttributesChanged(PrintAttributes) you will receive
- * one or more calls to {@link #onPrint(List, FileDescriptor, CancellationSignal,
- * PrintResultCallback)} asking you to write a PDF file with the content for
- * specific pages.
- * </li>
- * <li>
- * Finally, you will receive a call on {@link #onFinish()} right after printing.
- * You can use this callback to release resources.
- * </li>
- * <li>
- * You can receive calls to {@link #getInfo()} at any point after a call to
- * {@link #onPrintAttributesChanged(PrintAttributes)} which should return
- * a {@link PrintAdapterInfo} describing your {@link PrintAdapter}.
- * </li>
- * </ul>
- * </p>
- * <p>
- */
-public abstract class PrintAdapter {
-
-    /**
-     * Called when printing started. You can use this callback to
-     * allocate resources.
-     * <p>
-     * <strong>Note:</strong> Invoked on the main thread.
-     * </p>
-     */
-    public void onStart() {
-        /* do nothing - stub */
-    }
-
-    /**
-     * Called when the print job attributes (page size, density, etc)
-     * changed giving you a chance to re-layout the content such that
-     * it matches the new constraints.
-     * <p>
-     * <strong>Note:</strong> Invoked on the main thread.
-     * </p>
-     *
-     * @param attributes The print job attributes.
-     * @return Whether the content changed based on the provided attributes.
-     */
-    public boolean onPrintAttributesChanged(PrintAttributes attributes) {
-        return false;
-    }
-
-    /**
-     * Called when specific pages of the content have to be printed in the from of
-     * a PDF file to the given file descriptor. You should <strong>not</strong>
-     * close the file descriptor instead you have to invoke {@link PrintResultCallback
-     * #onPrintFinished()} or {@link PrintResultCallback#onPrintFailed(CharSequence)}.
-     * <p>
-     * <strong>Note:</strong> If the printed content is large, it is a  good
-     * practice to schedule writing it on a dedicated thread and register a
-     * callback in the provided {@link CancellationSignal} upon invocation of
-     * which you should stop writing data. The cancellation callback will not
-     * be made on the main thread.
-     * </p>
-     * <p>
-     * <strong>Note:</strong> Invoked on the main thread.
-     * </p>
-     *
-     * @param pages The pages whose content to print.
-     * @param destination The destination file descriptor to which to start writing.
-     * @param cancellationSignal Signal for observing cancel print requests.
-     * @param progressListener Callback to inform the system with the write progress.
-     *
-     * @see CancellationSignal
-     */
-    public abstract void onPrint(List<PageRange> pages, FileDescriptor destination,
-            CancellationSignal cancellationSignal, PrintResultCallback progressListener);
-
-    /**
-     * Called when printing finished. You can use this callback to release
-     * resources.
-     * <p>
-     * <strong>Note:</strong> Invoked on the main thread.
-     * </p>
-     */
-    public void onFinish() {
-        /* do nothing - stub */
-    }
-
-    /**
-     * Gets a {@link PrinterInfo} object that contains metadata about the
-     * printed content.
-     * <p>
-     * <strong>Note:</strong> Invoked on the main thread.
-     * </p>
-     *
-     * @return The info object for this {@link PrintAdapter}.
-     *
-     * @see PrintAdapterInfo
-     */
-    public abstract PrintAdapterInfo getInfo();
-
-    /**
-     * Base class for implementing a listener for the print result
-     * of a {@link PrintAdapter}.
-     */
-    public static abstract class PrintResultCallback {
-
-        PrintResultCallback() {
-            /* do nothing - hide constructor */
-        }
-
-        /**
-         * Notifies that all the data was printed.
-         *
-         * @param pages The pages that were printed.
-         */
-        public void onPrintFinished(List<PageRange> pages) {
-            /* do nothing - stub */
-        }
-
-        /**
-         * Notifies that an error occurred while printing the data.
-         *
-         * @param error Error message. May be null if error is unknown.
-         */
-        public void onPrintFailed(CharSequence error) {
-            /* do nothing - stub */
-        }
-    }
-}
diff --git a/core/java/android/print/PrintAdapterInfo.java b/core/java/android/print/PrintAdapterInfo.java
deleted file mode 100644
index 06e6b10..0000000
--- a/core/java/android/print/PrintAdapterInfo.java
+++ /dev/null
@@ -1,136 +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 android.print;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * This class encapsulates information about a {@link PrintAdapter} object.
- */
-public final class PrintAdapterInfo implements Parcelable {
-
-    /**
-     * Constant for unknown page count.
-     */
-    public static final int PAGE_COUNT_UNKNOWN = -1;
-
-    private int mPageCount;
-    private int mFlags;
-
-    /**
-     * Creates a new instance.
-     */
-    private PrintAdapterInfo() {
-        /* do nothing */
-    }
-
-    /**
-     * Creates a new instance.
-     *
-     * @param parcel Data from which to initialize.
-     */
-    private PrintAdapterInfo(Parcel parcel) {
-        mPageCount = parcel.readInt();
-        mFlags = parcel.readInt();
-    }
-
-    /**
-     * Gets the total number of pages.
-     *
-     * @return The number of pages.
-     */
-    public int getPageCount() {
-        return mPageCount;
-    }
-
-    /**
-     * @return The flags of this printable info.
-     *
-     * @see #FLAG_NOTIFY_FOR_ATTRIBUTES_CHANGE
-     */
-    public int getFlags() {
-        return mFlags;
-    }
-
-    @Override
-    public int describeContents() {
-        return 0;
-    }
-
-    @Override
-    public void writeToParcel(Parcel parcel, int flags) {
-        parcel.writeInt(mPageCount);
-        parcel.writeInt(mFlags);
-    }
-
-    /**
-     * Builder for creating an {@link PrintAdapterInfo}.
-     */
-    public static final class Builder {
-        private final PrintAdapterInfo mPrintableInfo = new PrintAdapterInfo();
-
-        /**
-         * Sets the total number of pages.
-         *
-         * @param pageCount The number of pages. Must be
-         * greater than zero.
-         */
-        public Builder setPageCount(int pageCount) {
-            if (pageCount < 0) {
-                throw new IllegalArgumentException("pageCount"
-                        + " must be greater than or euqal to zero!");
-            }
-            mPrintableInfo.mPageCount = pageCount;
-            return this;
-        }
-
-        /**
-         * Sets the flags of this printable info.
-         *
-         * @param flags The flags.
-         *
-         * @see #FLAG_NOTIFY_FOR_ATTRIBUTES_CHANGE
-         */
-        public Builder setFlags(int flags) {
-            mPrintableInfo.mFlags = flags;
-            return this;
-        }
-
-        /**
-         * Creates a new {@link PrintAdapterInfo} instance.
-         *
-         * @return The new instance.
-         */
-        public PrintAdapterInfo create() {
-            return mPrintableInfo;
-        }
-    }
-
-    public static final Parcelable.Creator<PrintAdapterInfo> CREATOR =
-            new Creator<PrintAdapterInfo>() {
-        @Override
-        public PrintAdapterInfo createFromParcel(Parcel parcel) {
-            return new PrintAdapterInfo(parcel);
-        }
-
-        @Override
-        public PrintAdapterInfo[] newArray(int size) {
-            return new PrintAdapterInfo[size];
-        }
-    };
-}
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index 8511d0b..2a27a32 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -18,11 +18,10 @@
 
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.res.Resources.NotFoundException;
+import android.content.res.Resources;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
-import android.util.Log;
 
 import com.android.internal.R;
 
@@ -388,145 +387,506 @@
      * This class specifies a supported media size.
      */
     public static final class MediaSize {
-        private static final String LOG_TAG = "MediaSize";
 
         // TODO: Verify media sizes and add more standard ones.
 
         // ISO sizes
 
-        /** ISO A0 media size: 841mm x 1189mm (33.11" x 46.81") */
-        public static final MediaSize ISO_A0 =
-                new MediaSize("ISO_A0", "android", R.string.mediaSize_iso_a0, 33110, 46810);
-        /** ISO A1 media size: 594mm x 841mm (23.39" x 33.11") */
-        public static final MediaSize ISO_A1 =
-                new MediaSize("ISO_A1", "android", R.string.mediaSize_iso_a1, 23390, 33110);
-        /** ISO A2 media size: 420mm x 594mm (16.54" x 23.39") */
-        public static final MediaSize ISO_A2 =
-                new MediaSize("ISO_A2", "android", R.string.mediaSize_iso_a2, 16540, 23390);
-        /** ISO A3 media size: 297mm x 420mm (11.69" x 16.54") */
-        public static final MediaSize ISO_A3 =
-                new MediaSize("ISO_A3", "android", R.string.mediaSize_iso_a3, 11690, 16540);
-        /** ISO A4 media size: 210mm x 297mm (8.27" x 11.69") */
-        public static final MediaSize ISO_A4 =
-                new MediaSize("ISO_A4", "android", R.string.mediaSize_iso_a4, 8270, 11690);
-        /** ISO A5 media size: 148mm x 210mm (5.83" x 8.27") */
-        public static final MediaSize ISO_A5 =
-                new MediaSize("ISO_A5", "android", R.string.mediaSize_iso_a5, 5830, 8270);
-        /** ISO A6 media size: 105mm x 148mm (4.13" x 5.83") */
-        public static final MediaSize ISO_A6 =
-                new MediaSize("ISO_A6", "android", R.string.mediaSize_iso_a6, 4130, 5830);
-        /** ISO A7 media size: 74mm x 105mm (2.91" x 4.13") */
-        public static final MediaSize ISO_A7 =
-                new MediaSize("ISO_A7", "android", R.string.mediaSize_iso_a7, 2910, 4130);
-        /** ISO A8 media size: 52mm x 74mm (2.05" x 2.91") */
-        public static final MediaSize ISO_A8 =
-                new MediaSize("ISO_A8", "android", R.string.mediaSize_iso_a8, 2050, 2910);
-        /** ISO A9 media size: 37mm x 52mm (1.46" x 2.05") */
-        public static final MediaSize ISO_A9 =
-                new MediaSize("ISO_A9", "android", R.string.mediaSize_iso_a9, 1460, 2050);
-        /** ISO A10 media size: 26mm x 37mm (1.02" x 1.46") */
-        public static final MediaSize ISO_A10 =
-                new MediaSize("ISO_A10", "android", R.string.mediaSize_iso_a10, 1020, 1460);
+        /**
+         * ISO A0 media size: 841mm x 1189mm (33.11" x 46.81")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_A0 = 1;
 
-        /** ISO B0 media size: 1000mm x 1414mm (39.37" x 55.67") */
-        public static final MediaSize ISO_B0 =
-                new MediaSize("ISO_B0", "android", R.string.mediaSize_iso_b0, 39370, 55670);
-        /** ISO B1 media size: 707mm x 1000mm (27.83" x 39.37") */
-        public static final MediaSize ISO_B1 =
-                new MediaSize("ISO_B1", "android", R.string.mediaSize_iso_b1, 27830, 39370);
-        /** ISO B2 media size: 500mm x 707mm (19.69" x 27.83") */
-        public static final MediaSize ISO_B2 =
-                new MediaSize("ISO_B2", "android", R.string.mediaSize_iso_b2, 19690, 27830);
-        /** ISO B3 media size: 353mm x 500mm (13.90" x 19.69") */
-        public static final MediaSize ISO_B3 =
-                new MediaSize("ISO_B3", "android", R.string.mediaSize_iso_b3, 13900, 19690);
-        /** ISO B4 media size: 250mm x 353mm (9.84" x 13.90") */
-        public static final MediaSize ISO_B4 =
-                new MediaSize("ISO_B4", "android", R.string.mediaSize_iso_b4, 9840, 13900);
-        /** ISO B5 media size: 176mm x 250mm (6.93" x 9.84") */
-        public static final MediaSize ISO_B5 =
-                new MediaSize("ISO_B5", "android", R.string.mediaSize_iso_b5, 6930, 9840);
-        /** ISO B6 media size: 125mm x 176mm (4.92" x 6.93") */
-        public static final MediaSize ISO_B6 =
-                new MediaSize("ISO_B6", "android", R.string.mediaSize_iso_b6, 4920, 6930);
-        /** ISO B7 media size: 88mm x 125mm (3.46" x 4.92") */
-        public static final MediaSize ISO_B7 =
-                new MediaSize("ISO_B7", "android", R.string.mediaSize_iso_b7, 3460, 4920);
-        /** ISO B8 media size: 62mm x 88mm (2.44" x 3.46") */
-        public static final MediaSize ISO_B8 =
-                new MediaSize("ISO_B8", "android", R.string.mediaSize_iso_b8, 2440, 3460);
-        /** ISO B9 media size: 44mm x 62mm (1.73" x 2.44") */
-        public static final MediaSize ISO_B9 =
-                new MediaSize("ISO_B9", "android", R.string.mediaSize_iso_b9, 1730, 2440);
-        /** ISO B10 media size: 31mm x 44mm (1.22" x 1.73") */
-        public static final MediaSize ISO_B10 =
-                new MediaSize("ISO_B10", "android", R.string.mediaSize_iso_b10, 1220, 1730);
+        /**
+         * ISO A1 media size: 594mm x 841mm (23.39" x 33.11")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_A1 = 2;
 
-        /** ISO C0 media size: 917mm x 1297mm (36.10" x 51.06") */
-        public static final MediaSize ISO_C0 =
-                new MediaSize("ISO_C0", "android", R.string.mediaSize_iso_c0, 36100, 51060);
-        /** ISO C1 media size: 648mm x 917mm (25.51" x 36.10") */
-        public static final MediaSize ISO_C1 =
-                new MediaSize("ISO_C1", "android", R.string.mediaSize_iso_c1, 25510, 36100);
-        /** ISO C2 media size: 458mm x 648mm (18.03" x 25.51") */
-        public static final MediaSize ISO_C2 =
-                new MediaSize("ISO_C2", "android", R.string.mediaSize_iso_c2, 18030, 25510);
-        /** ISO C3 media size: 324mm x 458mm (12.76" x 18.03") */
-        public static final MediaSize ISO_C3 =
-                new MediaSize("ISO_C3", "android", R.string.mediaSize_iso_c3, 12760, 18030);
-        /** ISO C4 media size: 229mm x 324mm (9.02" x 12.76") */
-        public static final MediaSize ISO_C4 =
-                new MediaSize("ISO_C4", "android", R.string.mediaSize_iso_c4, 9020, 12760);
-        /** ISO C5 media size: 162mm x 229mm (6.38" x 9.02") */
-        public static final MediaSize ISO_C5 =
-                new MediaSize("ISO_C5", "android", R.string.mediaSize_iso_c5, 6380, 9020);
-        /** ISO C6 media size: 114mm x 162mm (4.49" x 6.38") */
-        public static final MediaSize ISO_C6 =
-                new MediaSize("ISO_C6", "android", R.string.mediaSize_iso_c6, 4490, 6380);
-        /** ISO C7 media size: 81mm x 114mm (3.19" x 4.49") */
-        public static final MediaSize ISO_C7 =
-                new MediaSize("ISO_C7", "android", R.string.mediaSize_iso_c7, 3190, 4490);
-        /** ISO C8 media size: 57mm x 81mm (2.24" x 3.19") */
-        public static final MediaSize ISO_C8 =
-                new MediaSize("ISO_C8", "android", R.string.mediaSize_iso_c8, 2240, 3190);
-        /** ISO C9 media size: 40mm x 57mm (1.57" x 2.24") */
-        public static final MediaSize ISO_C9 =
-                new MediaSize("ISO_C9", "android", R.string.mediaSize_iso_c9, 1570, 2240);
-        /** ISO C10 media size: 28mm x 40mm (1.10" x 1.57") */
-        public static final MediaSize ISO_C10 =
-                new MediaSize("ISO_C10", "android", R.string.mediaSize_iso_c10, 1100, 1570);
+        /**
+         *
+         *ISO A2 media size: 420mm x 594mm (16.54" x 23.39")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_A2 = 3;
+
+        /**
+         * ISO A3 media size: 297mm x 420mm (11.69" x 16.54")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_A3 = 4;
+
+        /**
+         * ISO A4 media size: 210mm x 297mm (8.27" x 11.69")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_A4 = 5;
+
+        /**
+         * ISO A5 media size: 148mm x 210mm (5.83" x 8.27")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_A5 = 6;
+
+        /**
+         * ISO A6 media size: 105mm x 148mm (4.13" x 5.83")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_A6 = 7;
+
+        /**
+         * ISO A7 media size: 74mm x 105mm (2.91" x 4.13")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_A7 = 8;
+
+        /**
+         * ISO A8 media size: 52mm x 74mm (2.05" x 2.91")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_A8 = 9;
+
+        /**
+         * ISO A9 media size: 37mm x 52mm (1.46" x 2.05")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_A9 = 10;
+
+        /**
+         * ISO A10 media size: 26mm x 37mm (1.02" x 1.46")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_A10 = 11;
+
+
+        /**
+         * ISO B0 media size: 1000mm x 1414mm (39.37" x 55.67")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_B0 = 100;
+
+        /**
+         * ISO B1 media size: 707mm x 1000mm (27.83" x 39.37")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_B1 = 101;
+
+        /**
+         * ISO B2 media size: 500mm x 707mm (19.69" x 27.83")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_B2 = 102;
+
+        /**
+         * ISO B3 media size: 353mm x 500mm (13.90" x 19.69")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_B3 = 103;
+
+        /**
+         * ISO B4 media size: 250mm x 353mm (9.84" x 13.90")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_B4 = 104;
+
+        /**
+         * ISO B5 media size: 176mm x 250mm (6.93" x 9.84")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_B5 = 105;
+
+        /**
+         * ISO B6 media size: 125mm x 176mm (4.92" x 6.93")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_B6 = 106;
+
+        /**
+         * ISO B7 media size: 88mm x 125mm (3.46" x 4.92")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_B7 = 107;
+
+        /** ISO B8 media size: 62mm x 88mm (2.44" x 3.46")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_B8 = 108;
+
+        /**
+         * ISO B9 media size: 44mm x 62mm (1.73" x 2.44")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_B9 = 109;
+
+        /**
+         * ISO B10 media size: 31mm x 44mm (1.22" x 1.73")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_B10 = 110;
+
+
+        /**
+         * ISO C0 media size: 917mm x 1297mm (36.10" x 51.06")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_C0 = 200;
+
+        /**
+         * ISO C1 media size: 648mm x 917mm (25.51" x 36.10")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+
+        public static final int ISO_C1 = 201;
+        /**
+         * ISO C2 media size: 458mm x 648mm (18.03" x 25.51")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_C2 = 202;
+
+        /**
+         * ISO C3 media size: 324mm x 458mm (12.76" x 18.03")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_C3 = 203;
+
+        /**
+         * ISO C4 media size: 229mm x 324mm (9.02" x 12.76")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_C4 = 204;
+
+        /**
+         * ISO C5 media size: 162mm x 229mm (6.38" x 9.02")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_C5 = 205;
+
+        /**
+         * ISO C6 media size: 114mm x 162mm (4.49" x 6.38")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_C6 = 206;
+
+        /**
+         * ISO C7 media size: 81mm x 114mm (3.19" x 4.49")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_C7 = 207;
+
+        /**
+         * ISO C8 media size: 57mm x 81mm (2.24" x 3.19")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_C8 = 208;
+
+        /**
+         * ISO C9 media size: 40mm x 57mm (1.57" x 2.24")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_C9 = 209;
+
+        /**
+         * ISO C10 media size: 28mm x 40mm (1.10" x 1.57")
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int ISO_C10 = 210;
+
 
         // North America
 
-        /** North America Letter media size: 8.5" x 11" */
-        public static final MediaSize NA_LETTER =
-                new MediaSize("NA_LETTER", "android", R.string.mediaSize_na_letter, 8500, 11000);
-        /** North America Government-Letter media size: 8.0" x 10.5" */
-        public static final MediaSize NA_GOVT_LETTER =
-                new MediaSize("NA_GOVT_LETTER", "android",
-                        R.string.mediaSize_na_gvrnmt_letter, 8000, 10500);
-        /** North America Legal media size: 8.5" x 14" */
-        public static final MediaSize NA_LEGAL =
-                new MediaSize("NA_LEGAL", "android", R.string.mediaSize_na_legal, 8500, 14000);
-        /** North America Junior Legal media size: 8.0" x 5.0" */
-        public static final MediaSize NA_JUNIOR_LEGAL =
-                new MediaSize("NA_JUNIOR_LEGAL", "android",
-                        R.string.mediaSize_na_junior_legal, 8000, 5000);
-        /** North America Ledger media size: 17" x 11" */
-        public static final MediaSize NA_LEDGER =
-                new MediaSize("NA_LEDGER", "android", R.string.mediaSize_na_ledger, 17000, 11000);
-        /** North America Tabloid media size: 11" x 17" */
-        public static final MediaSize NA_TBLOID =
-                new MediaSize("NA_TABLOID", "android",
-                        R.string.mediaSize_na_tabloid, 11000, 17000);
+        /**
+         * North America Letter media size: 8.5" x 11"
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int NA_LETTER = 300;
+
+        /**
+         * North America Government-Letter media size: 8.0" x 10.5"
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int NA_GOVT_LETTER = 301;
+
+        /**
+         * North America Legal media size: 8.5" x 14"
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int NA_LEGAL = 302;
+
+        /**
+         * North America Junior Legal media size: 8.0" x 5.0"
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int NA_JUNIOR_LEGAL = 303;
+
+        /**
+         * North America Ledger media size: 17" x 11"
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int NA_LEDGER = 304;
+
+        /**
+         * North America Tabloid media size: 11" x 17"
+         *
+         * @see #createMediaSize(PackageManager, int)
+         */
+        public static final int NA_TBLOID = 305;
+
+        /**
+         * Creates a standard media size with a localized label.
+         *
+         * @param pm Package manager used to load the label.
+         * @param mediaSize Media size constant.
+         * @return A {@link MediaSize} instance with a localized label.
+         */
+        public static MediaSize createMediaSize(PackageManager pm, int mediaSize) {
+            final Resources resources;
+            try {
+                resources = pm.getResourcesForApplication("android");
+            } catch (NameNotFoundException nnfe) {
+                return null;
+            }
+            switch (mediaSize) {
+                case ISO_A0: {
+                    return new MediaSize("ISO_A0", resources
+                            .getString(R.string.mediaSize_iso_a0), 33110, 46810);
+                }
+                case ISO_A1: {
+                    return new MediaSize("ISO_A1", resources
+                            .getString(R.string.mediaSize_iso_a1), 23390, 33110);
+                }
+                case ISO_A2: {
+                    return new MediaSize("ISO_A2", resources
+                            .getString(R.string.mediaSize_iso_a2), 16540, 23390);
+                }
+                case ISO_A3: {
+                    return new MediaSize("ISO_A3", resources
+                            .getString(R.string.mediaSize_iso_a3), 11690, 16540);
+                }
+                case ISO_A4: {
+                    return new MediaSize("ISO_A4", resources
+                            .getString(R.string.mediaSize_iso_a4), 8270, 11690);
+                }
+                case ISO_A5: {
+                    return new MediaSize("ISO_A5", resources
+                            .getString(R.string.mediaSize_iso_a5), 5830, 8270);
+                }
+                case ISO_A6: {
+                    return new MediaSize("ISO_A6", resources
+                            .getString(R.string.mediaSize_iso_a6), 4130, 5830);
+                }
+                case ISO_A7: {
+                    return new MediaSize("ISO_A7", resources
+                            .getString(R.string.mediaSize_iso_a7), 2910, 4130);
+                }
+                case ISO_A8: {
+                    return new MediaSize("ISO_A8", resources
+                            .getString(R.string.mediaSize_iso_a8), 2050, 2910);
+                }
+                case ISO_A9: {
+                    return new MediaSize("ISO_A9", resources
+                            .getString(R.string.mediaSize_iso_a9), 1460, 2050);
+                }
+                case ISO_A10: {
+                    return new MediaSize("ISO_A10", resources
+                            .getString(R.string.mediaSize_iso_a10), 1020, 1460);
+                }
+                case ISO_B0: {
+                    return new MediaSize("ISO_B0", resources
+                            .getString(R.string.mediaSize_iso_b0), 39370, 55670);
+                }
+                case ISO_B1: {
+                    return new MediaSize("ISO_B1", resources
+                            .getString(R.string.mediaSize_iso_b1), 27830, 39370);
+                }
+                case ISO_B2: {
+                    return new MediaSize("ISO_B2", resources
+                            .getString(R.string.mediaSize_iso_b2), 19690, 27830);
+                }
+                case ISO_B3: {
+                    return new MediaSize("ISO_B3", resources
+                            .getString(R.string.mediaSize_iso_b3), 13900, 19690);
+                }
+                case ISO_B4: {
+                    return new MediaSize("ISO_B4", resources
+                            .getString(R.string.mediaSize_iso_b4), 9840, 13900);
+                }
+                case ISO_B5: {
+                    return new MediaSize("ISO_B5", resources
+                            .getString(R.string.mediaSize_iso_b5), 6930, 9840);
+                }
+                case ISO_B6: {
+                    return new MediaSize("ISO_B6", resources
+                            .getString(R.string.mediaSize_iso_b6), 4920, 6930);
+                }
+                case ISO_B7: {
+                    return new MediaSize("ISO_B7", resources
+                            .getString(R.string.mediaSize_iso_b7), 3460, 4920);
+                }
+                case ISO_B8: {
+                    return new MediaSize("ISO_B8", resources
+                            .getString(R.string.mediaSize_iso_b8), 2440, 3460);
+                }
+                case ISO_B9: {
+                    return new MediaSize("ISO_B9", resources
+                            .getString(R.string.mediaSize_iso_b9), 1730, 2440);
+                }
+                case ISO_B10: {
+                    return new MediaSize("ISO_B10", resources
+                            .getString(R.string.mediaSize_iso_b10), 1220, 1730);
+                }
+                case ISO_C0: {
+                    return new MediaSize("ISO_C0", resources
+                            .getString(R.string.mediaSize_iso_c0), 36100, 51060);
+                }
+                case ISO_C1: {
+                    return new MediaSize("ISO_C1", resources
+                            .getString(R.string.mediaSize_iso_c1), 25510, 36100);
+                }
+                case ISO_C2: {
+                    return new MediaSize("ISO_C2", resources
+                            .getString(R.string.mediaSize_iso_c2), 18030, 25510);
+                }
+                case ISO_C3: {
+                    return new MediaSize("ISO_C3", resources
+                            .getString(R.string.mediaSize_iso_c3), 12760, 18030);
+                }
+                case ISO_C4: {
+                    return new MediaSize("ISO_C4", resources
+                            .getString(R.string.mediaSize_iso_c4), 9020, 12760);
+                }
+                case ISO_C5: {
+                    return new MediaSize("ISO_C5", resources
+                            .getString(R.string.mediaSize_iso_c5), 6380, 9020);
+                }
+                case ISO_C6: {
+                    return new MediaSize("ISO_C6", resources
+                            .getString(R.string.mediaSize_iso_c6), 4490, 6380);
+                }
+                case ISO_C7: {
+                    return new MediaSize("ISO_C7", resources
+                            .getString(R.string.mediaSize_iso_c7), 3190, 4490);
+                }
+                case ISO_C8: {
+                    return new MediaSize("ISO_C8", resources
+                            .getString(R.string.mediaSize_iso_c8), 2240, 3190);
+                }
+                case ISO_C9: {
+                    return new MediaSize("ISO_C9", resources
+                            .getString(R.string.mediaSize_iso_c9), 1570, 2240);
+                }
+                case ISO_C10: {
+                    return new MediaSize("ISO_C10", resources
+                            .getString(R.string.mediaSize_iso_c10), 1100, 1570);
+                }
+                case NA_LETTER: {
+                    return new MediaSize("NA_LETTER", resources
+                            .getString(R.string.mediaSize_na_letter), 8500, 11000);
+                }
+                case NA_GOVT_LETTER: {
+                    return new MediaSize("NA_GOVT_LETTER", resources
+                            .getString(R.string.mediaSize_na_gvrnmt_letter), 8000, 10500);
+                }
+                case NA_LEGAL: {
+                    return new MediaSize("NA_LEGAL", resources
+                            .getString(R.string.mediaSize_na_legal), 8500, 14000);
+                }
+                case NA_JUNIOR_LEGAL: {
+                    return new MediaSize("NA_JUNIOR_LEGAL", resources
+                            .getString(R.string.mediaSize_na_junior_legal), 8000, 5000);
+                }
+                case NA_LEDGER: {
+                    return new MediaSize("NA_LEDGER", resources
+                            .getString(R.string.mediaSize_na_ledger), 17000, 11000);
+                }
+                case NA_TBLOID: {
+                    return new MediaSize("NA_TABLOID", resources
+                            .getString(R.string.mediaSize_na_tabloid), 11000, 17000);
+                }
+                default: {
+                    throw new IllegalArgumentException("Unknown media size.");
+                }
+            }
+        }
 
         private final String mId;
-        private final String mPackageName;
-        private final int mLabelResId;
+        private final CharSequence mLabel;
         private final int mWidthMils;
         private final int mHeightMils;
 
         /**
+         * Creates a new instance.
+         *
+         * @param id The unique media size id.
+         * @param label The <strong>internationalized</strong> human readable label.
+         * @param widthMils The width in mils (thousands of an inch).
+         * @param heightMils The height in mils (thousands of an inch).
+         *
+         * @throws IllegalArgumentException If the id is empty.
+         * @throws IllegalArgumentException If the label is empty.
+         * @throws IllegalArgumentException If the widthMils is less than or equal to zero.
+         * @throws IllegalArgumentException If the heightMils is less than or equal to zero.
+         */
+        public MediaSize(String id, CharSequence label, int widthMils, int heightMils) {
+            if (TextUtils.isEmpty(id)) {
+                throw new IllegalArgumentException("id cannot be empty.");
+            }
+            if (TextUtils.isEmpty(label)) {
+                throw new IllegalArgumentException("label cannot be empty.");
+            }
+            if (widthMils <= 0) {
+                throw new IllegalArgumentException("widthMils "
+                        + "cannot be less than or equal to zero.");
+            }
+            if (heightMils <= 0) {
+                throw new IllegalArgumentException("heightMils "
+                       + "cannot be less than or euqual to zero.");
+            }
+            mId = id;
+            mLabel = label;
+            mWidthMils = widthMils;
+            mHeightMils = heightMils;
+        }
+
+        /**
          * Gets the unique media size id.
          *
          * @return The unique media size id.
@@ -540,18 +900,8 @@
          *
          * @return The human readable label.
          */
-        public CharSequence getLabel(PackageManager packageManager) {
-            try {
-                return packageManager.getResourcesForApplication(
-                        mPackageName).getString(mLabelResId);
-            } catch (NotFoundException nfe) {
-                Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
-                        + " from package " + mPackageName);
-            } catch (NameNotFoundException nnfee) {
-                Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
-                        + " from package " + mPackageName);
-            }
-            return null;
+        public CharSequence getLabel() {
+            return mLabel;
         }
 
         /**
@@ -572,50 +922,9 @@
             return mHeightMils;
         }
 
-        /**
-         * Creates a new instance.
-         *
-         * @param id The unique media size id.
-         * @param packageName The name of the creating package.
-         * @param labelResId The resource if of a human readable label.
-         * @param widthMils The width in mils (thousands of an inch).
-         * @param heightMils The height in mils (thousands of an inch).
-         *
-         * @throws IllegalArgumentException If the id is empty.
-         * @throws IllegalArgumentException If the label is empty.
-         * @throws IllegalArgumentException If the widthMils is less than or equal to zero.
-         * @throws IllegalArgumentException If the heightMils is less than or equal to zero.
-         */
-        public MediaSize(String id, String packageName, int labelResId,
-                int widthMils, int heightMils) {
-            if (TextUtils.isEmpty(id)) {
-                throw new IllegalArgumentException("id cannot be empty.");
-            }
-            if (TextUtils.isEmpty(packageName)) {
-                throw new IllegalArgumentException("packageName cannot be empty.");
-            }
-            if (labelResId <= 0) {
-                throw new IllegalArgumentException("labelResId must be greater than zero.");
-            }
-            if (widthMils <= 0) {
-                throw new IllegalArgumentException("widthMils "
-                        + "cannot be less than or equal to zero.");
-            }
-            if (heightMils <= 0) {
-                throw new IllegalArgumentException("heightMils "
-                       + "cannot be less than or euqual to zero.");
-            }
-            mPackageName = packageName;
-            mId = id;
-            mLabelResId = labelResId;
-            mWidthMils = widthMils;
-            mHeightMils = heightMils;
-        }
-
         void writeToParcel(Parcel parcel) {
             parcel.writeString(mId);
-            parcel.writeString(mPackageName);
-            parcel.writeInt(mLabelResId);
+            parcel.writeCharSequence(mLabel);
             parcel.writeInt(mWidthMils);
             parcel.writeInt(mHeightMils);
         }
@@ -623,8 +932,7 @@
         static MediaSize createFromParcel(Parcel parcel) {
             return new MediaSize(
                     parcel.readString(),
-                    parcel.readString(),
-                    parcel.readInt(),
+                    parcel.readCharSequence(),
                     parcel.readInt(),
                     parcel.readInt());
         }
@@ -634,8 +942,7 @@
             StringBuilder builder = new StringBuilder();
             builder.append("MediaSize{");
             builder.append("id: ").append(mId);
-            builder.append(", packageName: ").append(mPackageName);
-            builder.append(", labelResId: ").append(mLabelResId);
+            builder.append(", label: ").append(mLabel);
             builder.append(", heightMils: ").append(mHeightMils);
             builder.append(", widthMils: ").append(mWidthMils);
             builder.append("}");
@@ -647,15 +954,46 @@
      * This class specifies a supported resolution in dpi (dots per inch).
      */
     public static final class Resolution {
-        private static final String LOG_TAG = "Resolution";
-
         private final String mId;
-        private final String mPackageName;
-        private final int mLabelResId;
+        private final CharSequence mLabel;
         private final int mHorizontalDpi;
         private final int mVerticalDpi;
 
         /**
+         * Creates a new instance.
+         *
+         * @param id The unique resolution id.
+         * @param label The <strong>internationalized</strong> human readable label.
+         * @param horizontalDpi The horizontal resolution in dpi.
+         * @param verticalDpi The vertical resolution in dpi.
+         *
+         * @throws IllegalArgumentException If the id is empty.
+         * @throws IllegalArgumentException If the label is empty.
+         * @throws IllegalArgumentException If the horizontalDpi is less than or equal to zero.
+         * @throws IllegalArgumentException If the verticalDpi is less than or equal to zero.
+         */
+        public Resolution(String id, CharSequence label, int horizontalDpi, int verticalDpi) {
+            if (TextUtils.isEmpty(id)) {
+                throw new IllegalArgumentException("id cannot be empty.");
+            }
+            if (TextUtils.isEmpty(label)) {
+                throw new IllegalArgumentException("label cannot be empty.");
+            }
+            if (horizontalDpi <= 0) {
+                throw new IllegalArgumentException("horizontalDpi "
+                        + "cannot be less than or equal to zero.");
+            }
+            if (verticalDpi <= 0) {
+                throw new IllegalArgumentException("verticalDpi"
+                       + " cannot be less than or equal to zero.");
+            }
+            mId = id;
+            mLabel = label;
+            mHorizontalDpi = horizontalDpi;
+            mVerticalDpi = verticalDpi;
+        }
+
+        /**
          * Gets the unique resolution id.
          *
          * @return The unique resolution id.
@@ -670,17 +1008,7 @@
          * @return The human readable label.
          */
         public CharSequence getLabel(PackageManager packageManager) {
-            try {
-                return packageManager.getResourcesForApplication(
-                        mPackageName).getString(mLabelResId);
-            } catch (NotFoundException nfe) {
-                Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
-                        + " from package " + mPackageName);
-            } catch (NameNotFoundException nnfee) {
-                Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
-                        + " from package " + mPackageName);
-            }
-            return null;
+            return mLabel;
         }
 
         /**
@@ -701,50 +1029,9 @@
             return mVerticalDpi;
         }
 
-        /**
-         * Creates a new instance.
-         *
-         * @param id The unique resolution id.
-         * @param packageName The name of the creating package.
-         * @param labelResId The resource id of a human readable label.
-         * @param horizontalDpi The horizontal resolution in dpi.
-         * @param verticalDpi The vertical resolution in dpi.
-         *
-         * @throws IllegalArgumentException If the id is empty.
-         * @throws IllegalArgumentException If the label is empty.
-         * @throws IllegalArgumentException If the horizontalDpi is less than or equal to zero.
-         * @throws IllegalArgumentException If the verticalDpi is less than or equal to zero.
-         */
-        public Resolution(String id, String packageName, int labelResId,
-                int horizontalDpi, int verticalDpi) {
-            if (TextUtils.isEmpty(id)) {
-                throw new IllegalArgumentException("id cannot be empty.");
-            }
-            if (TextUtils.isEmpty(packageName)) {
-                throw new IllegalArgumentException("packageName cannot be empty.");
-            }
-            if (labelResId <= 0) {
-                throw new IllegalArgumentException("labelResId must be greater than zero.");
-            }
-            if (horizontalDpi <= 0) {
-                throw new IllegalArgumentException("horizontalDpi "
-                        + "cannot be less than or equal to zero.");
-            }
-            if (verticalDpi <= 0) {
-                throw new IllegalArgumentException("verticalDpi"
-                       + " cannot be less than or equal to zero.");
-            }
-            mId = id;
-            mPackageName = packageName;
-            mLabelResId = labelResId;
-            mHorizontalDpi = horizontalDpi;
-            mVerticalDpi = verticalDpi;
-        }
-
         void writeToParcel(Parcel parcel) {
             parcel.writeString(mId);
-            parcel.writeString(mPackageName);
-            parcel.writeInt(mLabelResId);
+            parcel.writeCharSequence(mLabel);
             parcel.writeInt(mHorizontalDpi);
             parcel.writeInt(mVerticalDpi);
         }
@@ -752,8 +1039,7 @@
         static Resolution createFromParcel(Parcel parcel) {
             return new Resolution(
                     parcel.readString(),
-                    parcel.readString(),
-                    parcel.readInt(),
+                    parcel.readCharSequence(),
                     parcel.readInt(),
                     parcel.readInt());
         }
@@ -763,8 +1049,7 @@
             StringBuilder builder = new StringBuilder();
             builder.append("Resolution{");
             builder.append("id: ").append(mId);
-            builder.append(", packageName: ").append(mPackageName);
-            builder.append(", labelResId: ").append(mLabelResId);
+            builder.append(", label: ").append(mLabel);
             builder.append(", horizontalDpi: ").append(mHorizontalDpi);
             builder.append(", verticalDpi: ").append(mVerticalDpi);
             builder.append("}");
@@ -782,6 +1067,38 @@
         private final int mBottomMils;
 
         /**
+         * Creates a new instance.
+         *
+         * @param leftMils The left margin in mils (thousands of an inch).
+         * @param topMils The top margin in mils (thousands of an inch).
+         * @param rightMils The right margin in mils (thousands of an inch).
+         * @param bottomMils The bottom margin in mils (thousands of an inch).
+         *
+         * @throws IllegalArgumentException If the leftMils is less than zero.
+         * @throws IllegalArgumentException If the topMils is less than zero.
+         * @throws IllegalArgumentException If the rightMils is less than zero.
+         * @throws IllegalArgumentException If the bottomMils is less than zero.
+         */
+        public Margins(int leftMils, int topMils, int rightMils, int bottomMils) {
+            if (leftMils < 0) {
+                throw new IllegalArgumentException("leftMils cannot be less than zero.");
+            }
+            if (topMils < 0) {
+                throw new IllegalArgumentException("topMils cannot be less than zero.");
+            }
+            if (rightMils < 0) {
+                throw new IllegalArgumentException("rightMils cannot be less than zero.");
+            }
+            if (bottomMils < 0) {
+                throw new IllegalArgumentException("bottomMils cannot be less than zero.");
+            }
+            mTopMils = topMils;
+            mLeftMils = leftMils;
+            mRightMils = rightMils;
+            mBottomMils = bottomMils;
+        }
+
+        /**
          * Gets the left margin in mils (thousands of an inch).
          *
          * @return The left margin.
@@ -817,38 +1134,6 @@
             return mBottomMils;
         }
 
-        /**
-         * Creates a new instance.
-         *
-         * @param leftMils The left margin in mils (thousands of an inch).
-         * @param topMils The top margin in mils (thousands of an inch).
-         * @param rightMils The right margin in mils (thousands of an inch).
-         * @param bottomMils The bottom margin in mils (thousands of an inch).
-         *
-         * @throws IllegalArgumentException If the leftMils is less than zero.
-         * @throws IllegalArgumentException If the topMils is less than zero.
-         * @throws IllegalArgumentException If the rightMils is less than zero.
-         * @throws IllegalArgumentException If the bottomMils is less than zero.
-         */
-        public Margins(int leftMils, int topMils, int rightMils, int bottomMils) {
-            if (leftMils < 0) {
-                throw new IllegalArgumentException("leftMils cannot be less than zero.");
-            }
-            if (topMils < 0) {
-                throw new IllegalArgumentException("topMils cannot be less than zero.");
-            }
-            if (rightMils < 0) {
-                throw new IllegalArgumentException("rightMils cannot be less than zero.");
-            }
-            if (bottomMils < 0) {
-                throw new IllegalArgumentException("bottomMils cannot be less than zero.");
-            }
-            mTopMils = topMils;
-            mLeftMils = leftMils;
-            mRightMils = rightMils;
-            mBottomMils = bottomMils;
-        }
-
         void writeToParcel(Parcel parcel) {
             parcel.writeInt(mLeftMils);
             parcel.writeInt(mTopMils);
@@ -881,11 +1166,28 @@
      * Represents a printer tray.
      */
     public static final class Tray {
-        private static final String LOG_TAG = "Tray";
-
         private final String mId;
-        private final String mPackageName;
-        private final int mLabelResId;
+        private final CharSequence mLabel;
+
+        /**
+         * Creates a new instance.
+         *
+         * @param id The unique tray id.
+         * @param label The <strong>internationalized</strong> human readable label.
+         *
+         * @throws IllegalArgumentException If the id is empty.
+         * @throws IllegalArgumentException If the label is empty.
+         */
+        public Tray(String id, CharSequence label) {
+            if (TextUtils.isEmpty(id)) {
+                throw new IllegalArgumentException("id cannot be empty.");
+            }
+            if (TextUtils.isEmpty(label)) {
+                throw new IllegalArgumentException("label cannot be empty.");
+            }
+            mId = id;
+            mLabel = label;
+        }
 
         /**
          * Gets the unique tray id.
@@ -902,55 +1204,18 @@
          * @return The human readable label.
          */
         public CharSequence getLabel(PackageManager packageManager) {
-            try {
-                return packageManager.getResourcesForApplication(
-                        mPackageName).getString(mLabelResId);
-            } catch (NotFoundException nfe) {
-                Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
-                        + " from package " + mPackageName);
-            } catch (NameNotFoundException nnfee) {
-                Log.w(LOG_TAG, "Could not load resouce" + mLabelResId
-                        + " from package " + mPackageName);
-            }
-            return null;
-        }
-
-        /**
-         * Creates a new instance.
-         *
-         * @param id The unique tray id.
-         * @param packageName The name of the creating package.
-         * @param labelResId The resource id of a human readable label.
-         *
-         * @throws IllegalArgumentException If the id is empty.
-         * @throws IllegalArgumentException If the label is empty.
-         */
-        public Tray(String id, String packageName, int labelResId) {
-            if (TextUtils.isEmpty(id)) {
-                throw new IllegalArgumentException("id cannot be empty.");
-            }
-            if (TextUtils.isEmpty(packageName)) {
-                throw new IllegalArgumentException("packageName cannot be empty.");
-            }
-            if (labelResId <= 0) {
-                throw new IllegalArgumentException("label must be greater than zero.");
-            }
-            mId = id;
-            mPackageName = packageName;
-            mLabelResId = labelResId;
+            return mLabel;
         }
 
         void writeToParcel(Parcel parcel) {
             parcel.writeString(mId);
-            parcel.writeString(mPackageName);
-            parcel.writeInt(mLabelResId);
+            parcel.writeCharSequence(mLabel);
         }
 
         static Tray createFromParcel(Parcel parcel) {
             return new Tray(
                     parcel.readString(),
-                    parcel.readString(),
-                    parcel.readInt());
+                    parcel.readCharSequence());
         }
 
         @Override
@@ -959,8 +1224,7 @@
             builder.append("Tray{");
             builder.append("id: ").append(mId);
             builder.append("id: ").append(mId);
-            builder.append(", packageName: ").append(mPackageName);
-            builder.append(", labelResId: ").append(mLabelResId);
+            builder.append(", label: ").append(mLabel);
             builder.append("}");
             return builder.toString();
         }
diff --git a/core/java/android/print/PrintDocumentAdapter.java b/core/java/android/print/PrintDocumentAdapter.java
new file mode 100644
index 0000000..ef69400
--- /dev/null
+++ b/core/java/android/print/PrintDocumentAdapter.java
@@ -0,0 +1,200 @@
+/*
+ * 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 android.print;
+
+import android.os.CancellationSignal;
+
+import java.io.FileDescriptor;
+import java.util.List;
+
+/**
+ * Base class that provides the content of a document to be printed.
+ *
+ * <h3>Lifecycle</h3>
+ * <p>
+ * <ul>
+ * <li>
+ * Initially, you will receive a call to {@link #onStart()}. This callback
+ * can be used to allocate resources.
+ * </li>
+ * <li>
+ * Next, you will get one or more calls to {@link #onLayout(PrintAttributes,
+ * PrintAttributes, CancellationSignal, LayoutResultCallback)} to inform you
+ * that the print attributes (page size, density, etc) changed giving you an
+ * opportunity to layout the content to match the new constraints.
+ * </li>
+ * <li>
+ * After every call to {@link #onLayout(PrintAttributes, PrintAttributes,
+ * CancellationSignal, LayoutResultCallback)}, you may get a call to {@link
+ * #onWrite(List, FileDescriptor, CancellationSignal, WriteResultCallback)}
+ * asking you to write a PDF file with the content for specific pages.
+ * </li>
+ * <li>
+ * Finally, you will receive a call to {@link #onFinish()}. You can use this
+ * callback to release resources allocated in {@link #onStart()}.
+ * </li>
+ * </ul>
+ * </p>
+ */
+public abstract class PrintDocumentAdapter {
+
+    /**
+     * Called when printing starts. You can use this callback to allocate
+     * resources. This method is invoked on the main thread.
+     */
+    public void onStart() {
+        /* do nothing - stub */
+    }
+
+    /**
+     * Called when the print attributes (page size, density, etc) changed
+     * giving you a chance to layout the content such that it matches the
+     * new constraints. This method is invoked on the main thread.
+     * <p>
+     * After you are done laying out, you must invoke: {@link LayoutResultCallback
+     * #onLayoutFinished(PrintDocumentInfo, boolean)} with the last argument <code>true
+     * </code> or <code>false</code> depending on whether the layout changed the
+     * content or not, respectively; and {@link LayoutResultCallback#onLayoutFailed(
+     * CharSequence), if an error occurred.
+     * </p>
+     * <p>
+     * <strong>Note:</strong> If the content is large and a layout will be
+     * performed, it is a good practice to schedule the work on a dedicated
+     * thread and register an observer in the provided {@link
+     * CancellationSignal} upon invocation of which you should stop the
+     * layout. The cancellation callback will not be made on the main
+     * thread.
+     * </p>
+     *
+     * @param oldAttributes The old print attributes.
+     * @param newAttributes The new print attributes.
+     * @param cancellationSignal Signal for observing cancel layout requests.
+     * @param callback Callback to inform the system for the layout result.
+     *
+     * @see LayoutResultCallback
+     * @see CancellationSignal
+     */
+    public abstract void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+            CancellationSignal cancellationSignal, LayoutResultCallback callback);
+
+    /**
+     * Called when specific pages of the content should be written in the
+     * 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()}, 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
+     * practice to schedule writing it on a dedicated thread and register an
+     * observer in the provided {@link CancellationSignal} upon invocation of
+     * which you should stop writing. The cancellation callback will not be
+     * made on the main thread.
+     * </p>
+     *
+     * @param pages The pages whose content to print.
+     * @param destination The destination file descriptor to which to write.
+     * @param cancellationSignal Signal for observing cancel writing requests.
+     * @param callback Callback to inform the system for the write result.
+     *
+     * @see WriteResultCallback
+     * @see CancellationSignal
+     */
+    public abstract void onWrite(List<PageRange> pages, FileDescriptor destination,
+            CancellationSignal cancellationSignal, WriteResultCallback callback);
+
+    /**
+     * Called when printing finishes. You can use this callback to release
+     * resources acquired in {@link #onStart()}. This method is invoked on
+     * the main thread.
+     */
+    public void onFinish() {
+        /* do nothing - stub */
+    }
+
+    /**
+     * Base class for implementing a callback for the result of {@link
+     * PrintDocumentAdapter#onWrite(List, FileDescriptor, CancellationSignal,
+     * WriteResultCallback)}.
+     */
+    public static abstract class WriteResultCallback {
+
+        /**
+         * @hide
+         */
+        public WriteResultCallback() {
+            /* do nothing - hide constructor */
+        }
+
+        /**
+         * Notifies that all the data was written.
+         *
+         * @param pages The pages that were written.
+         */
+        public void onWriteFinished(List<PageRange> pages) {
+            /* do nothing - stub */
+        }
+
+        /**
+         * Notifies that an error occurred while writing the data.
+         *
+         * @param error Error message. May be null if error is unknown.
+         */
+        public void onWriteFailed(CharSequence error) {
+            /* do nothing - stub */
+        }
+    }
+
+    /**
+     * Base class for implementing a callback for the result of {@link
+     * PrintDocumentAdapter#onLayout(PrintAttributes, PrintAttributes,
+     * CancellationSignal, LayoutResultCallback)}.
+     */
+    public static abstract class LayoutResultCallback {
+
+        /**
+         * @hide
+         */
+        public LayoutResultCallback() {
+            /* do nothing - hide constructor */
+        }
+
+        /**
+         * Notifies that the layout finished and whether the content changed.
+         *
+         * @param info An info object describing the document.
+         * @param changed Whether the layout changed.
+         *
+         * @see PrintDocumentInfo
+         */
+        public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+            /* do nothing - stub */
+        }
+
+        /**
+         * Notifies that an error occurred while laying out the document.
+         *
+         * @param error Error message. May be null if error is unknown.
+         */
+        public void onLayoutFailed(CharSequence error) {
+            /* do nothing - stub */
+        }
+    }
+}
diff --git a/core/java/android/print/PrintAdapterInfo.aidl b/core/java/android/print/PrintDocumentInfo.aidl
similarity index 95%
rename from core/java/android/print/PrintAdapterInfo.aidl
rename to core/java/android/print/PrintDocumentInfo.aidl
index 27bf717..831dcb7 100644
--- a/core/java/android/print/PrintAdapterInfo.aidl
+++ b/core/java/android/print/PrintDocumentInfo.aidl
@@ -16,4 +16,4 @@
 
 package android.print;
 
-parcelable PrintAdapterInfo;
+parcelable PrintDocumentInfo;
diff --git a/core/java/android/print/PrintDocumentInfo.java b/core/java/android/print/PrintDocumentInfo.java
new file mode 100644
index 0000000..7731deb
--- /dev/null
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -0,0 +1,171 @@
+/*
+ * 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 android.print;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * This class encapsulates information about a printed document.
+ */
+public final class PrintDocumentInfo implements Parcelable {
+
+    /**
+     * Constant for unknown page count (default).
+     */
+    public static final int PAGE_COUNT_UNKNOWN = -1;
+
+    /**
+     * Content type: unknown (default).
+     */
+    public static final int CONTENT_TYPE_UNKNOWN = -1;
+
+    /**
+     * Content type: document.
+     */
+    public static final int CONTENT_TYPE_DOCUMENT = 0;
+
+    /**
+     * Content type: photo.
+     */
+    public static final int CONTENT_TYPE_PHOTO = 1;
+
+    private int mPageCount;
+    private int mContentType;
+
+    /**
+     * Creates a new instance.
+     */
+    private PrintDocumentInfo() {
+        mPageCount = PAGE_COUNT_UNKNOWN;
+        mContentType = CONTENT_TYPE_UNKNOWN;
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param Prototype from which to clone.
+     */
+    private PrintDocumentInfo(PrintDocumentInfo prototype) {
+        mPageCount = prototype.mPageCount;
+        mContentType = prototype.mContentType;
+    }
+
+    /**
+     * Creates a new instance.
+     *
+     * @param parcel Data from which to initialize.
+     */
+    private PrintDocumentInfo(Parcel parcel) {
+        mPageCount = parcel.readInt();
+        mContentType = parcel.readInt();
+    }
+
+    /**
+     * Gets the total number of pages.
+     *
+     * @return The number of pages.
+     *
+     * @see #PAGE_COUNT_UNKNOWN
+     */
+    public int getPageCount() {
+        return mPageCount;
+    }
+
+    /**
+     * Gets the content type.
+     *
+     * @return The content type.
+     *
+     * @see #CONTENT_TYPE_UNKNOWN
+     * @see #CONTENT_TYPE_DOCUMENT
+     * @see #CONTENT_TYPE_PHOTO
+     */
+    public int getContentType() {
+        return mContentType;
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel parcel, int flags) {
+        parcel.writeInt(mPageCount);
+        parcel.writeInt(mContentType);
+    }
+
+    /**
+     * Builder for creating an {@link PrintDocumentInfo}.
+     */
+    public static final class Builder {
+        private final PrintDocumentInfo mPrototype = new PrintDocumentInfo();
+
+        /**
+         * Sets the total number of pages.
+         *
+         * @param pageCount The number of pages. Must be greater than
+         * or equal to zero or {@link PrintDocumentInfo#PAGE_COUNT_UNKNOWN}.
+         */
+        public Builder setPageCount(int pageCount) {
+            if (pageCount < 0 && pageCount != PAGE_COUNT_UNKNOWN) {
+                throw new IllegalArgumentException("pageCount"
+                        + " must be greater than or euqal to zero or"
+                        + " DocumentInfo#PAGE_COUNT_UNKNOWN");
+            }
+            mPrototype.mPageCount = pageCount;
+            return this;
+        }
+
+        /**
+         * Sets the content type.
+         *
+         * @param type The content type.
+         *
+         * @see #CONTENT_TYPE_UNKNOWN
+         * @see #CONTENT_TYPE_DOCUMENT
+         * @see #CONTENT_TYPE_PHOTO
+         */
+        public Builder setContentType(int type) {
+            mPrototype.mContentType = type;
+            return this;
+        }
+
+        /**
+         * Creates a new {@link PrintDocumentInfo} instance.
+         *
+         * @return The new instance.
+         */
+        public PrintDocumentInfo create() {
+            return new PrintDocumentInfo(mPrototype);
+        }
+    }
+
+    public static final Parcelable.Creator<PrintDocumentInfo> CREATOR =
+            new Creator<PrintDocumentInfo>() {
+        @Override
+        public PrintDocumentInfo createFromParcel(Parcel parcel) {
+            return new PrintDocumentInfo(parcel);
+        }
+
+        @Override
+        public PrintDocumentInfo[] newArray(int size) {
+            return new PrintDocumentInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/print/PrintFileAdapter.java b/core/java/android/print/PrintFileAdapter.java
deleted file mode 100644
index dab9648..0000000
--- a/core/java/android/print/PrintFileAdapter.java
+++ /dev/null
@@ -1,125 +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 android.print;
-
-import android.os.AsyncTask;
-import android.os.CancellationSignal;
-import android.os.CancellationSignal.OnCancelListener;
-import android.util.Log;
-
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Adapter for printing files.
- */
-class PrintFileAdapter extends PrintAdapter {
-
-    private static final String LOG_TAG = "PrintFileAdapter";
-
-    private final File mFile;
-
-    private WriteFileAsyncTask mWriteFileAsyncTask;
-
-    public PrintFileAdapter(File file) {
-        if (file == null) {
-            throw new IllegalArgumentException("File cannot be null!");
-        }
-        mFile = file;
-    }
-
-    @Override
-    public void onPrint(List<PageRange> pages, FileDescriptor destination,
-            CancellationSignal cancellationSignal, PrintResultCallback callback) {
-        mWriteFileAsyncTask = new WriteFileAsyncTask(mFile, destination, cancellationSignal,
-                callback);
-        mWriteFileAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,
-                (Void[]) null);
-        
-    }
-
-    @Override
-    public PrintAdapterInfo getInfo() {
-        // TODO: When we have PDF render library we should query the page count.
-        return new PrintAdapterInfo.Builder().create();
-    }
-
-    private static final class WriteFileAsyncTask extends AsyncTask<Void, Void, Void> {
-
-        private final File mSource;
-
-        private final FileDescriptor mDestination;
-
-        private final PrintResultCallback mResultCallback;
-
-        private final CancellationSignal mCancellationSignal;
-
-        public WriteFileAsyncTask(File source, FileDescriptor destination,
-                CancellationSignal cancellationSignal, PrintResultCallback callback) {
-            mSource = source;
-            mDestination = destination;
-            mResultCallback = callback;
-            mCancellationSignal = cancellationSignal; 
-            mCancellationSignal.setOnCancelListener(new OnCancelListener() {
-                @Override
-                public void onCancel() {
-                    cancel(true);
-                }
-            });
-        }
-
-        @Override
-        protected Void doInBackground(Void... params) {
-            InputStream in = null;
-            OutputStream out = new FileOutputStream(mDestination);
-            final byte[] buffer = new byte[8192];
-            try {
-                in = new FileInputStream(mSource);
-                while (true) {
-                    final int readByteCount = in.read(buffer);
-                    if (readByteCount < 0) {
-                        break;
-                    }
-                    out.write(buffer, 0, readByteCount);
-                }
-             } catch (IOException ioe) {
-                Log.e(LOG_TAG, "Error writing data!", ioe);
-             } finally {
-                IoUtils.closeQuietly(in);
-                IoUtils.closeQuietly(out);
-                if (!isCancelled()) {
-                    List<PageRange> pages = new ArrayList<PageRange>();
-                    pages.add(PageRange.ALL_PAGES);
-                    mResultCallback.onPrintFinished(pages);
-                } else {
-                    mResultCallback.onPrintFailed("Cancelled");
-                }
-            }
-            return null;
-        }
-    }
-}
-
diff --git a/core/java/android/print/PrintJob.java b/core/java/android/print/PrintJob.java
index f7cca87..a5e0b79 100644
--- a/core/java/android/print/PrintJob.java
+++ b/core/java/android/print/PrintJob.java
@@ -55,7 +55,7 @@
      * @return The print job info.
      */
     public PrintJobInfo getInfo() {
-        PrintJobInfo info = mPrintManager.getPrintJob(mId);
+        PrintJobInfo info = mPrintManager.getPrintJobInfo(mId);
         if (info != null) {
             mCachedInfo = info;
         }
diff --git a/core/java/android/print/PrintJobInfo.java b/core/java/android/print/PrintJobInfo.java
index 72d6057..6e613bc 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -116,6 +116,44 @@
     /** The print job attributes size. */
     private PrintAttributes mAttributes;
 
+    /** Information about the printed document. */
+    private PrintDocumentInfo mDocumentInfo;
+
+    /** @hide*/
+    public PrintJobInfo() {
+        /* do nothing */
+    }
+
+    /** @hide */
+    public PrintJobInfo(PrintJobInfo other) {
+        mId = other.mId;
+        mLabel = other.mLabel;
+        mPrinterId = other.mPrinterId;
+        mState = other.mState;
+        mAppId = other.mAppId;
+        mUserId = other.mUserId;
+        mAttributes = other.mAttributes;
+        mDocumentInfo = other.mDocumentInfo;
+    }
+
+    private PrintJobInfo(Parcel parcel) {
+        mId = parcel.readInt();
+        mLabel = parcel.readCharSequence();
+        mPrinterId = parcel.readParcelable(null);
+        mState = parcel.readInt();
+        mAppId = parcel.readInt();
+        mUserId = parcel.readInt();
+        if (parcel.readInt() == 1) {
+            mPageRanges = (PageRange[]) parcel.readParcelableArray(null);
+        }
+        if (parcel.readInt() == 1) {
+            mAttributes = PrintAttributes.CREATOR.createFromParcel(parcel);
+        }
+        if (parcel.readInt() == 1) {
+            mDocumentInfo = PrintDocumentInfo.CREATOR.createFromParcel(parcel);
+        }
+    }
+
     /**
      * Gets the unique print job id.
      *
@@ -300,35 +338,26 @@
         mAttributes = attributes;
     }
 
-    /** @hide*/
-    public PrintJobInfo() {
-        /* do nothing */
+    /**
+     * Gets the info describing the printed document.
+     *
+     * @return The document info.
+     *
+     * @hide
+     */
+    public PrintDocumentInfo getDocumentInfo() {
+        return mDocumentInfo;
     }
 
-    /** @hide */
-    public PrintJobInfo(PrintJobInfo other) {
-        mId = other.mId;
-        mLabel = other.mLabel;
-        mPrinterId = other.mPrinterId;
-        mState = other.mState;
-        mAppId = other.mAppId;
-        mUserId = other.mUserId;
-        mAttributes = other.mAttributes;
-    }
-
-    private PrintJobInfo(Parcel parcel) {
-        mId = parcel.readInt();
-        mLabel = parcel.readCharSequence();
-        mPrinterId = parcel.readParcelable(null);
-        mState = parcel.readInt();
-        mAppId = parcel.readInt();
-        mUserId = parcel.readInt();
-        if (parcel.readInt() == 1) {
-            mPageRanges = (PageRange[]) parcel.readParcelableArray(null);
-        }
-        if (parcel.readInt() == 1) {
-            mAttributes = PrintAttributes.CREATOR.createFromParcel(parcel);
-        }
+    /**
+     * Sets the info describing the printed document.
+     *
+     * @param info The document info.
+     *
+     * @hide
+     */
+    public void setDocumentInfo(PrintDocumentInfo info) {
+        mDocumentInfo = info;
     }
 
     @Override
@@ -356,6 +385,12 @@
         } else {
             parcel.writeInt(0);
         }
+        if (mDocumentInfo != null) {
+            parcel.writeInt(1);
+            mDocumentInfo.writeToParcel(parcel, flags);
+        } else {
+            parcel.writeInt(0);
+        }
     }
 
     @Override
@@ -366,7 +401,10 @@
         builder.append(", id: ").append(mId);
         builder.append(", status: ").append(stateToString(mState));
         builder.append(", printer: " + mPrinterId);
-        builder.append(", attributes: " + (mAttributes != null ? mAttributes.toString() : null));
+        builder.append(", attributes: " + (mAttributes != null
+                ? mAttributes.toString() : null));
+        builder.append(", documentInfo: " + (mDocumentInfo != null
+                ? mDocumentInfo.toString() : null));
         builder.append("}");
         return builder.toString();
     }
diff --git a/core/java/android/print/PrintManager.java b/core/java/android/print/PrintManager.java
index be9b596..5ca19d4 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -26,7 +26,8 @@
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.print.PrintAdapter.PrintResultCallback;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
 import android.util.Log;
 
 import com.android.internal.os.SomeArgs;
@@ -103,7 +104,7 @@
      * Creates an instance that can access all print jobs.
      *
      * @param userId The user id for which to get all print jobs.
-     * @return An instance of the caller has the permission to access
+     * @return An instance if the caller has the permission to access
      * all print jobs, null otherwise.
      *
      * @hide
@@ -112,11 +113,11 @@
         return new PrintManager(mContext, mService, userId, APP_ID_ANY);
     }
 
-    PrintJobInfo getPrintJob(int printJobId) {
+    PrintJobInfo getPrintJobInfo(int printJobId) {
         try {
-            return mService.getPrintJob(printJobId, mAppId, mUserId);
+            return mService.getPrintJobInfo(printJobId, mAppId, mUserId);
         } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error getting print job:" + printJobId, re);
+            Log.e(LOG_TAG, "Error getting a print job info:" + printJobId, re);
         }
         return null;
     }
@@ -130,7 +131,7 @@
      */
     public List<PrintJob> getPrintJobs() {
         try {
-            List<PrintJobInfo> printJobInfos = mService.getPrintJobs(mAppId, mUserId);
+            List<PrintJobInfo> printJobInfos = mService.getPrintJobInfos(mAppId, mUserId);
             if (printJobInfos == null) {
                 return Collections.emptyList();
             }
@@ -141,18 +142,17 @@
             }
             return printJobs;
         } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error getting print jobs!", re);
+            Log.e(LOG_TAG, "Error getting print jobs", re);
         }
         return Collections.emptyList();
     }
 
-    ICancellationSignal cancelPrintJob(int printJobId) {
+    void cancelPrintJob(int printJobId) {
         try {
             mService.cancelPrintJob(printJobId, mAppId, mUserId);
         } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error cancleing a print job:" + printJobId, re);
+            Log.e(LOG_TAG, "Error cancleing a print job: " + printJobId, re);
         }
-        return null;
     }
 
     /**
@@ -166,24 +166,24 @@
      * @see PrintJob
      */
     public PrintJob print(String printJobName, File pdfFile, PrintAttributes attributes) {
-        PrintFileAdapter printable = new PrintFileAdapter(pdfFile);
-        return print(printJobName, printable, attributes);
+        FileDocumentAdapter documentAdapter = new FileDocumentAdapter(mContext, pdfFile);
+        return print(printJobName, documentAdapter, attributes);
     }
 
     /**
-     * Creates a print job for printing a {@link PrintAdapter} with default print
+     * Creates a print job for printing a {@link PrintDocumentAdapter} with default print
      * attributes.
      *
      * @param printJobName A name for the new print job.
-     * @param printAdapter The printable adapter to print.
+     * @param documentAdapter An adapter that emits the document to print.
      * @param attributes The default print job attributes.
      * @return The created print job.
      *
      * @see PrintJob
      */
-    public PrintJob print(String printJobName, PrintAdapter printAdapter,
+    public PrintJob print(String printJobName, PrintDocumentAdapter documentAdapter,
             PrintAttributes attributes) {
-        PrintAdapterDelegate delegate = new PrintAdapterDelegate(printAdapter,
+        PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
                 mContext.getMainLooper());
         try {
             PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate,
@@ -217,145 +217,118 @@
         }
     }
 
-    private static final class PrintAdapterDelegate extends IPrintAdapter.Stub {
-        private final Object mLock = new Object();
+    private static final class PrintDocumentAdapterDelegate extends IPrintDocumentAdapter.Stub {
+        private PrintDocumentAdapter mDocumentAdapter; // Strong reference OK - cleared in finish()
 
-        private PrintAdapter mPrintAdapter;
+        private Handler mHandler; // Strong reference OK - cleared in finish()
 
-        private Handler mHandler;
-
-        public PrintAdapterDelegate(PrintAdapter printAdapter, Looper looper) {
-            mPrintAdapter = printAdapter;
+        public PrintDocumentAdapterDelegate(PrintDocumentAdapter documentAdapter, Looper looper) {
+            mDocumentAdapter = documentAdapter;
             mHandler = new MyHandler(looper);
         }
 
         @Override
         public void start() {
-            synchronized (mLock) {
-                if (isFinishedLocked()) {
-                    return;
-                }
-                mHandler.obtainMessage(MyHandler.MESSAGE_START,
-                        mPrintAdapter).sendToTarget();
-            }
+            mHandler.sendEmptyMessage(MyHandler.MSG_START);
         }
 
         @Override
-        public void printAttributesChanged(PrintAttributes attributes) {
-            synchronized (mLock) {
-                if (isFinishedLocked()) {
-                    return;
-                }
-                SomeArgs args = SomeArgs.obtain();
-                args.arg1 = mPrintAdapter;
-                args.arg2 = attributes;
-                mHandler.obtainMessage(MyHandler.MESSAGE_PRINT_ATTRIBUTES_CHANGED,
-                        args).sendToTarget();
-            }
+        public void layout(PrintAttributes oldAttributes,
+            PrintAttributes newAttributes, ILayoutResultCallback callback) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = oldAttributes;
+            args.arg2 = newAttributes;
+            args.arg3 = callback;
+            mHandler.obtainMessage(MyHandler.MSG_LAYOUT, args).sendToTarget();
         }
 
         @Override
-        public void print(List<PageRange> pages, ParcelFileDescriptor fd,
-                IPrintResultCallback callback) {
-            synchronized (mLock) {
-                if (isFinishedLocked()) {
-                    return;
-                }
-                SomeArgs args = SomeArgs.obtain();
-                args.arg1 = mPrintAdapter;
-                args.arg2 = pages;
-                args.arg3 = fd.getFileDescriptor();
-                args.arg4 = callback;
-                mHandler.obtainMessage(MyHandler.MESSAGE_PRINT, args).sendToTarget();
-            }
+        public void write(List<PageRange> pages, ParcelFileDescriptor fd,
+            IWriteResultCallback callback) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = pages;
+            args.arg2 = fd.getFileDescriptor();
+            args.arg3 = callback;
+            mHandler.obtainMessage(MyHandler.MSG_WRITE, args).sendToTarget();
         }
 
         @Override
         public void finish() {
-            synchronized (mLock) {
-                if (isFinishedLocked()) {
-                    return;
-                }
-                mHandler.obtainMessage(MyHandler.MESSAGE_FINIS,
-                        mPrintAdapter).sendToTarget();
-            }
+            mHandler.sendEmptyMessage(MyHandler.MSG_FINISH);
         }
 
-        private boolean isFinishedLocked() {
-            return mPrintAdapter == null;
+        private boolean isFinished() {
+            return mDocumentAdapter == null;
         }
 
-        private void finishLocked() {
-            mPrintAdapter = null;
+        private void doFinish() {
+            mDocumentAdapter = null;
             mHandler = null;
         }
 
         private final class MyHandler extends Handler {
-            public static final int MESSAGE_START = 1;
-            public static final int MESSAGE_PRINT_ATTRIBUTES_CHANGED = 2;
-            public static final int MESSAGE_PRINT = 3;
-            public static final int MESSAGE_FINIS = 4;
+            public static final int MSG_START = 1;
+            public static final int MSG_LAYOUT = 2;
+            public static final int MSG_WRITE = 3;
+            public static final int MSG_FINISH = 4;
 
             public MyHandler(Looper looper) {
                 super(looper, null, true);
             }
 
             @Override
+            @SuppressWarnings("unchecked")
             public void handleMessage(Message message) {
+                if (isFinished()) {
+                    return;
+                }
                 switch (message.what) {
-                    case MESSAGE_START: {
-                        PrintAdapter adapter = (PrintAdapter) message.obj;
-                        adapter.onStart();
+                    case MSG_START: {
+                        mDocumentAdapter.onStart();
                     } break;
 
-                    case MESSAGE_PRINT_ATTRIBUTES_CHANGED: {
+                    case MSG_LAYOUT: {
                         SomeArgs args = (SomeArgs) message.obj;
-                        PrintAdapter adapter = (PrintAdapter) args.arg1;
-                        PrintAttributes attributes = (PrintAttributes) args.arg2;
+                        PrintAttributes oldAttributes = (PrintAttributes) args.arg1;
+                        PrintAttributes newAttributes = (PrintAttributes) args.arg2;
+                        ILayoutResultCallback callback = (ILayoutResultCallback) args.arg3;
                         args.recycle();
-                        adapter.onPrintAttributesChanged(attributes);
-                    } break;
 
-                    case MESSAGE_PRINT: {
-                        SomeArgs args = (SomeArgs) message.obj;
-                        PrintAdapter adapter = (PrintAdapter) args.arg1;
-                        @SuppressWarnings("unchecked")
-                        List<PageRange> pages = (List<PageRange>) args.arg2;
-                        final FileDescriptor fd = (FileDescriptor) args.arg3;
-                        IPrintResultCallback callback = (IPrintResultCallback) args.arg4;
-                        args.recycle();
                         try {
                             ICancellationSignal remoteSignal = CancellationSignal.createTransport();
-                            callback.onPrintStarted(adapter.getInfo(), remoteSignal);
+                            callback.onLayoutStarted(remoteSignal);
 
-                            CancellationSignal localSignal = CancellationSignal.fromTransport(
-                                    remoteSignal);
-                            adapter.onPrint(pages, fd, localSignal,
-                                    new PrintResultCallbackWrapper(callback) {
-                                        @Override
-                                        public void onPrintFinished(List<PageRange> pages) {
-                                            IoUtils.closeQuietly(fd);
-                                            super.onPrintFinished(pages);
-                                        }
+                            mDocumentAdapter.onLayout(oldAttributes, newAttributes,
+                                    CancellationSignal.fromTransport(remoteSignal),
+                                    new LayoutResultCallbackWrapper(callback));
+                        } catch (RemoteException re) {
+                            Log.e(LOG_TAG, "Error printing", re);
+                        }
+                    } break;
 
-                                        @Override
-                                        public void onPrintFailed(CharSequence error) {
-                                            IoUtils.closeQuietly(fd);
-                                            super.onPrintFailed(error);
-                                        }
-                                    });
+                    case MSG_WRITE: {
+                        SomeArgs args = (SomeArgs) message.obj;
+                        List<PageRange> pages = (List<PageRange>) args.arg1;
+                        FileDescriptor fd = (FileDescriptor) args.arg2;
+                        IWriteResultCallback callback = (IWriteResultCallback) args.arg3;
+                        args.recycle();
+
+                        try {
+                            ICancellationSignal remoteSignal = CancellationSignal.createTransport();
+                            callback.onWriteStarted(remoteSignal);
+
+                            mDocumentAdapter.onWrite(pages, fd,
+                                    CancellationSignal.fromTransport(remoteSignal),
+                                    new WriteResultCallbackWrapper(callback, fd));
                         } catch (RemoteException re) {
                             Log.e(LOG_TAG, "Error printing", re);
                             IoUtils.closeQuietly(fd);
                         }
                     } break;
 
-                    case MESSAGE_FINIS: {
-                        PrintAdapter adapter = (PrintAdapter) message.obj;
-                        adapter.onFinish();
-                        synchronized (mLock) {
-                            finishLocked();
-                        }
+                    case MSG_FINISH: {
+                        mDocumentAdapter.onFinish();
+                        doFinish();
                     } break;
 
                     default: {
@@ -367,29 +340,65 @@
         }
     }
 
-    private static abstract class PrintResultCallbackWrapper extends PrintResultCallback {
+    private static final class WriteResultCallbackWrapper extends WriteResultCallback {
 
-        private final IPrintResultCallback mWrappedCallback;
+        private final IWriteResultCallback mWrappedCallback;
+        private final FileDescriptor mFd;
 
-        public PrintResultCallbackWrapper(IPrintResultCallback callback) {
+        public WriteResultCallbackWrapper(IWriteResultCallback callback,
+                FileDescriptor fd) {
             mWrappedCallback = callback;
+            mFd = fd;
         }
 
         @Override
-        public void onPrintFinished(List<PageRange> pages) {
+        public void onWriteFinished(List<PageRange> pages) {
             try {
-                mWrappedCallback.onPrintFinished(pages);
+                // Close before notifying the other end. We want
+                // to be ready by the time we announce it.
+                IoUtils.closeQuietly(mFd);
+                mWrappedCallback.onWriteFinished(pages);
             } catch (RemoteException re) {
-                Log.e(LOG_TAG, "Error calling onPrintFinished", re);
+                Log.e(LOG_TAG, "Error calling onWriteFinished", re);
             }
         }
 
         @Override
-        public void onPrintFailed(CharSequence error) {
+        public void onWriteFailed(CharSequence error) {
             try {
-                mWrappedCallback.onPrintFailed(error);
+                // Close before notifying the other end. We want
+                // to be ready by the time we announce it.
+                IoUtils.closeQuietly(mFd);
+                mWrappedCallback.onWriteFailed(error);
             } catch (RemoteException re) {
-                Log.e(LOG_TAG, "Error calling onPrintFailed", re);
+                Log.e(LOG_TAG, "Error calling onWriteFailed", re);
+            }
+        }
+    }
+
+    private static final class LayoutResultCallbackWrapper extends LayoutResultCallback {
+
+        private final ILayoutResultCallback mWrappedCallback;
+
+        public LayoutResultCallbackWrapper(ILayoutResultCallback callback) {
+            mWrappedCallback = callback;
+        }
+
+        @Override
+        public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+            try {
+                mWrappedCallback.onLayoutFinished(info, changed);
+            } catch (RemoteException re) {
+                Log.e(LOG_TAG, "Error calling onLayoutFinished", re);
+            }
+        }
+
+        @Override
+        public void onLayoutFailed(CharSequence error) {
+            try {
+                mWrappedCallback.onLayoutFailed(error);
+            } catch (RemoteException re) {
+                Log.e(LOG_TAG, "Error calling onLayoutFailed", re);
             }
         }
     }
diff --git a/core/java/android/print/PrinterId.java b/core/java/android/print/PrinterId.java
index b853eb0..8a3148c 100644
--- a/core/java/android/print/PrinterId.java
+++ b/core/java/android/print/PrinterId.java
@@ -54,7 +54,7 @@
      *
      * @hide
      */
-    public ComponentName getServiceComponentName() {
+    public ComponentName getService() {
         return mServiceComponentName;
     }
 
diff --git a/core/java/android/print/PrinterInfo.java b/core/java/android/print/PrinterInfo.java
index 9283472..da3b6bc 100644
--- a/core/java/android/print/PrinterInfo.java
+++ b/core/java/android/print/PrinterInfo.java
@@ -86,6 +86,31 @@
         mDefaults.put(PROPERTY_ORIENTATION, DEFAULT_UNDEFINED);
     }
 
+    private PrinterInfo(PrinterInfo prototype) {
+        mId = prototype.mId;
+        mLabel = prototype.mLabel;
+        mStatus = prototype.mStatus;
+
+        mMinMargins = prototype.mMinMargins;
+        mMediaSizes.addAll(prototype.mMediaSizes);
+        mResolutions.addAll(prototype.mResolutions);
+        mInputTrays = (prototype.mInputTrays != null)
+                ? new ArrayList<Tray>(prototype.mInputTrays) : null;
+        mOutputTrays = (prototype.mOutputTrays != null)
+                ? new ArrayList<Tray>(prototype.mOutputTrays) : null;
+
+        mDuplexModes = prototype.mDuplexModes;
+        mColorModes = prototype.mColorModes;
+        mFittingModes = prototype.mFittingModes;
+        mOrientations = prototype.mOrientations;
+
+        final int defaultCount = prototype.mDefaults.size();
+        for (int i = 0; i < defaultCount; i++) {
+            mDefaults.put(prototype.mDefaults.keyAt(i), prototype.mDefaults.valueAt(i));
+        }
+        mDefaultMargins = prototype.mDefaultMargins;
+    }
+
     /**
      * Get the globally unique printer id.
      *
@@ -437,7 +462,7 @@
      * </p>
      */
     public static final class Builder {
-        private final PrinterInfo mPrinterInfo;
+        private final PrinterInfo mPrototype;
 
         /**
          * Creates a new instance.
@@ -455,9 +480,9 @@
             if (TextUtils.isEmpty(label)) {
                 throw new IllegalArgumentException("label cannot be empty.");
             }
-            mPrinterInfo = new PrinterInfo();
-            mPrinterInfo.mLabel = label;
-            mPrinterInfo.mId = printerId;
+            mPrototype = new PrinterInfo();
+            mPrototype.mLabel = label;
+            mPrototype.mId = printerId;
         }
 
         /**
@@ -470,7 +495,7 @@
          * @return This builder.
          */
         public Builder setStatus(int status) {
-            mPrinterInfo.mStatus = status;
+            mPrototype.mStatus = status;
             return this;
         }
 
@@ -489,11 +514,11 @@
          * @see PrintAttributes.MediaSize
          */
         public Builder addMediaSize(MediaSize mediaSize, boolean isDefault) {
-            final int insertionIndex = mPrinterInfo.mMediaSizes.size();
-            mPrinterInfo.mMediaSizes.add(mediaSize);
+            final int insertionIndex = mPrototype.mMediaSizes.size();
+            mPrototype.mMediaSizes.add(mediaSize);
             if (isDefault) {
                 throwIfDefaultAlreadySpecified(PROPERTY_MEDIA_SIZE);
-                mPrinterInfo.mDefaults.put(PROPERTY_MEDIA_SIZE, insertionIndex);
+                mPrototype.mDefaults.put(PROPERTY_MEDIA_SIZE, insertionIndex);
             }
             return this;
         }
@@ -514,11 +539,11 @@
          * @see PrintAttributes.Resolution
          */
         public Builder addResolution(Resolution resolution, boolean isDefault) {
-            final int insertionIndex = mPrinterInfo.mResolutions.size();
-            mPrinterInfo.mResolutions.add(resolution);
+            final int insertionIndex = mPrototype.mResolutions.size();
+            mPrototype.mResolutions.add(resolution);
             if (isDefault) {
                 throwIfDefaultAlreadySpecified(PROPERTY_RESOLUTION);
-                mPrinterInfo.mDefaults.put(PROPERTY_RESOLUTION, insertionIndex);
+                mPrototype.mDefaults.put(PROPERTY_RESOLUTION, insertionIndex);
             }
             return this;
         }
@@ -543,8 +568,8 @@
                 throw new IllegalArgumentException("Default margins"
                     + " cannot be outside of the min margins.");
             }
-            mPrinterInfo.mMinMargins = margins;
-            mPrinterInfo.mDefaultMargins = defaultMargins;
+            mPrototype.mMinMargins = margins;
+            mPrototype.mDefaultMargins = defaultMargins;
             return this;
         }
 
@@ -564,14 +589,14 @@
          * @see PrintAttributes.Tray
          */
         public Builder addInputTray(Tray inputTray, boolean isDefault) {
-            if (mPrinterInfo.mInputTrays == null) {
-                mPrinterInfo.mInputTrays = new ArrayList<Tray>();
+            if (mPrototype.mInputTrays == null) {
+                mPrototype.mInputTrays = new ArrayList<Tray>();
             }
-            final int insertionIndex = mPrinterInfo.mInputTrays.size();
-            mPrinterInfo.mInputTrays.add(inputTray);
+            final int insertionIndex = mPrototype.mInputTrays.size();
+            mPrototype.mInputTrays.add(inputTray);
             if (isDefault) {
                 throwIfDefaultAlreadySpecified(PROPERTY_INPUT_TRAY);
-                mPrinterInfo.mDefaults.put(PROPERTY_INPUT_TRAY, insertionIndex);
+                mPrototype.mDefaults.put(PROPERTY_INPUT_TRAY, insertionIndex);
             }
             return this;
         }
@@ -592,14 +617,14 @@
          * @see PrintAttributes.Tray
          */
         public Builder addOutputTray(Tray outputTray, boolean isDefault) {
-            if (mPrinterInfo.mOutputTrays == null) {
-                mPrinterInfo.mOutputTrays = new ArrayList<Tray>();
+            if (mPrototype.mOutputTrays == null) {
+                mPrototype.mOutputTrays = new ArrayList<Tray>();
             }
-            final int insertionIndex = mPrinterInfo.mOutputTrays.size();
-            mPrinterInfo.mOutputTrays.add(outputTray);
+            final int insertionIndex = mPrototype.mOutputTrays.size();
+            mPrototype.mOutputTrays.add(outputTray);
             if (isDefault) {
                 throwIfDefaultAlreadySpecified(PROPERTY_OUTPUT_TRAY);
-                mPrinterInfo.mDefaults.put(PROPERTY_OUTPUT_TRAY, insertionIndex);
+                mPrototype.mDefaults.put(PROPERTY_OUTPUT_TRAY, insertionIndex);
             }
             return this;
         }
@@ -631,8 +656,8 @@
                 throw new IllegalArgumentException("Default color mode not in color modes.");
             }
             PrintAttributes.enforceValidColorMode(colorModes);
-            mPrinterInfo.mColorModes = colorModes;
-            mPrinterInfo.mDefaults.put(PROPERTY_COLOR_MODE, defaultColorMode);
+            mPrototype.mColorModes = colorModes;
+            mPrototype.mDefaults.put(PROPERTY_COLOR_MODE, defaultColorMode);
             return this;
         }
 
@@ -664,8 +689,8 @@
                 throw new IllegalArgumentException("Default duplex mode not in duplex modes.");
             }
             PrintAttributes.enforceValidDuplexMode(defaultDuplexMode);
-            mPrinterInfo.mDuplexModes = duplexModes;
-            mPrinterInfo.mDefaults.put(PROPERTY_DUPLEX_MODE, defaultDuplexMode);
+            mPrototype.mDuplexModes = duplexModes;
+            mPrototype.mDefaults.put(PROPERTY_DUPLEX_MODE, defaultDuplexMode);
             return this;
         }
 
@@ -696,8 +721,8 @@
                 throw new IllegalArgumentException("Default fitting mode not in fiting modes.");
             }
             PrintAttributes.enfoceValidFittingMode(defaultFittingMode);
-            mPrinterInfo.mFittingModes = fittingModes;
-            mPrinterInfo.mDefaults.put(PROPERTY_FITTING_MODE, defaultFittingMode);
+            mPrototype.mFittingModes = fittingModes;
+            mPrototype.mDefaults.put(PROPERTY_FITTING_MODE, defaultFittingMode);
             return this;
         }
 
@@ -728,8 +753,8 @@
                 throw new IllegalArgumentException("Default orientation not in orientations.");
             }
             PrintAttributes.enforceValidOrientation(defaultOrientation);
-            mPrinterInfo.mOrientations = orientations;
-            mPrinterInfo.mDefaults.put(PROPERTY_ORIENTATION, defaultOrientation);
+            mPrototype.mOrientations = orientations;
+            mPrototype.mDefaults.put(PROPERTY_ORIENTATION, defaultOrientation);
             return this;
         }
 
@@ -743,41 +768,41 @@
          * @throws IllegalStateException If a required attribute was not specified.
          */
         public PrinterInfo create() {
-            if (mPrinterInfo.mMediaSizes == null || mPrinterInfo.mMediaSizes.isEmpty()) {
+            if (mPrototype.mMediaSizes == null || mPrototype.mMediaSizes.isEmpty()) {
                 throw new IllegalStateException("No media size specified.");
             }
-            if (mPrinterInfo.mDefaults.valueAt(PROPERTY_MEDIA_SIZE) == DEFAULT_UNDEFINED) {
+            if (mPrototype.mDefaults.valueAt(PROPERTY_MEDIA_SIZE) == DEFAULT_UNDEFINED) {
                 throw new IllegalStateException("No default media size specified.");
             }
-            if (mPrinterInfo.mResolutions == null || mPrinterInfo.mResolutions.isEmpty()) {
+            if (mPrototype.mResolutions == null || mPrototype.mResolutions.isEmpty()) {
                 throw new IllegalStateException("No resolution specified.");
             }
-            if (mPrinterInfo.mDefaults.valueAt(PROPERTY_RESOLUTION) == DEFAULT_UNDEFINED) {
+            if (mPrototype.mDefaults.valueAt(PROPERTY_RESOLUTION) == DEFAULT_UNDEFINED) {
                 throw new IllegalStateException("No default resolution specified.");
             }
-            if (mPrinterInfo.mColorModes == 0) {
+            if (mPrototype.mColorModes == 0) {
                 throw new IllegalStateException("No color mode specified.");
             }
-            if (mPrinterInfo.mDefaults.valueAt(PROPERTY_COLOR_MODE) == DEFAULT_UNDEFINED) {
+            if (mPrototype.mDefaults.valueAt(PROPERTY_COLOR_MODE) == DEFAULT_UNDEFINED) {
                 throw new IllegalStateException("No default color mode specified.");
             }
-            if (mPrinterInfo.mOrientations == 0) {
+            if (mPrototype.mOrientations == 0) {
                 throw new IllegalStateException("No oprientation specified.");
             }
-            if (mPrinterInfo.mDefaults.valueAt(PROPERTY_ORIENTATION) == DEFAULT_UNDEFINED) {
+            if (mPrototype.mDefaults.valueAt(PROPERTY_ORIENTATION) == DEFAULT_UNDEFINED) {
                 throw new IllegalStateException("No default orientation specified.");
             }
-            if (mPrinterInfo.mMinMargins == null) {
-                mPrinterInfo.mMinMargins  = new Margins(0, 0, 0, 0);
+            if (mPrototype.mMinMargins == null) {
+                mPrototype.mMinMargins  = new Margins(0, 0, 0, 0);
             }
-            if (mPrinterInfo.mDefaultMargins == null) {
-                mPrinterInfo.mDefaultMargins = mPrinterInfo.mMinMargins;
+            if (mPrototype.mDefaultMargins == null) {
+                mPrototype.mDefaultMargins = mPrototype.mMinMargins;
             }
-            return mPrinterInfo;
+            return new PrinterInfo(mPrototype);
         }
 
         private void throwIfDefaultAlreadySpecified(int propertyIndex) {
-            if (mPrinterInfo.mDefaults.get(propertyIndex) != DEFAULT_UNDEFINED) {
+            if (mPrototype.mDefaults.get(propertyIndex) != DEFAULT_UNDEFINED) {
                 throw new IllegalArgumentException("Default already specified.");
             }
         }
diff --git a/core/java/android/printservice/IPrintService.aidl b/core/java/android/printservice/IPrintService.aidl
index eabd96d..c72385a 100644
--- a/core/java/android/printservice/IPrintService.aidl
+++ b/core/java/android/printservice/IPrintService.aidl
@@ -17,6 +17,7 @@
 package android.printservice;
 
 import android.os.ICancellationSignal;
+import android.print.IPrinterDiscoveryObserver;
 import android.print.PrintJobInfo;
 import android.print.PrinterId;
 import android.printservice.IPrintServiceClient;
@@ -28,8 +29,8 @@
  */
 oneway interface IPrintService {
     void setClient(IPrintServiceClient client);
-    void requestCancelPrintJob(in PrintJobInfo printJob);
-    void onPrintJobQueued(in PrintJobInfo printJob);
-    void startPrinterDiscovery();
+    void requestCancelPrintJob(in PrintJobInfo printJobInfo);
+    void onPrintJobQueued(in PrintJobInfo printJobInfo);
+    void startPrinterDiscovery(IPrinterDiscoveryObserver observer);
     void stopPrinterDiscovery();
 }
diff --git a/core/java/android/printservice/IPrintServiceClient.aidl b/core/java/android/printservice/IPrintServiceClient.aidl
index cff8c02..cdde4d8 100644
--- a/core/java/android/printservice/IPrintServiceClient.aidl
+++ b/core/java/android/printservice/IPrintServiceClient.aidl
@@ -27,11 +27,9 @@
  * @hide
  */
 interface IPrintServiceClient {
-    List<PrintJobInfo> getPrintJobs();
-    PrintJobInfo getPrintJob(int printJobId);
+    List<PrintJobInfo> getPrintJobInfos();
+    PrintJobInfo getPrintJobInfo(int printJobId);
     boolean setPrintJobState(int printJobId, int status);
     boolean setPrintJobTag(int printJobId, String tag);
     oneway void writePrintJobData(in ParcelFileDescriptor fd, int printJobId);
-    oneway void addDiscoveredPrinters(in List<PrinterInfo> printers);
-    oneway void removeDiscoveredPrinters(in List<PrinterId> printers);
 }
diff --git a/core/java/android/printservice/PrintDocument.java b/core/java/android/printservice/PrintDocument.java
new file mode 100644
index 0000000..2a1581a
--- /dev/null
+++ b/core/java/android/printservice/PrintDocument.java
@@ -0,0 +1,91 @@
+/*
+ * 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 android.printservice;
+
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.PrintDocumentInfo;
+import android.util.Log;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+
+/**
+ * This class represents a printed document from the perspective of a print
+ * service. It exposes APIs to query the document and obtain its data.
+ */
+public final class PrintDocument {
+
+    private static final String LOG_TAG = "PrintDocument";
+
+    private final int mPrintJobId;
+
+    private final IPrintServiceClient mPrintServiceClient;
+
+    private final PrintDocumentInfo mInfo;
+
+    PrintDocument(int printJobId, IPrintServiceClient printServiceClient,
+            PrintDocumentInfo info) {
+        mPrintJobId = printJobId;
+        mPrintServiceClient = printServiceClient;
+        mInfo = info;
+    }
+
+    /**
+     * Gets the {@link PrintDocumentInfo} that describes this document.
+     *
+     * @return The document info.
+     */
+    public PrintDocumentInfo getInfo() {
+        return mInfo;
+    }
+
+    /**
+     * Gets the data associated with this document. It is a responsibility of the
+     * client to open a stream to the returned file descriptor and fully read the
+     * data.
+     * <p>
+     * <strong>Note:</strong> It is your responsibility to close the file descriptor.
+     * </p>
+     *
+     * @return A file descriptor for reading the data or <code>null</code>.
+     */
+    public FileDescriptor getData() {
+        ParcelFileDescriptor source = null;
+        ParcelFileDescriptor sink = null;
+        try {
+            ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
+            source = fds[0];
+            sink = fds[1];
+            mPrintServiceClient.writePrintJobData(sink, mPrintJobId);
+            return source.getFileDescriptor();
+        } catch (IOException ioe) {
+            Log.e(LOG_TAG, "Error calling getting print job data!", ioe);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error calling getting print job data!", re);
+        } finally {
+            if (sink != null) {
+                try {
+                    sink.close();
+                } catch (IOException ioe) {
+                    /* ignore */
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/core/java/android/printservice/PrintJob.java b/core/java/android/printservice/PrintJob.java
index f490f91..80530a7 100644
--- a/core/java/android/printservice/PrintJob.java
+++ b/core/java/android/printservice/PrintJob.java
@@ -16,10 +16,6 @@
 
 package android.printservice;
 
-import java.io.FileDescriptor;
-import java.io.IOException;
-
-import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 import android.print.PrintJobInfo;
 import android.util.Log;
@@ -33,19 +29,16 @@
 
     private static final String LOG_TAG = "PrintJob";
 
-    private final int mId;
-
     private final IPrintServiceClient mPrintServiceClient;
 
+    private final PrintDocument mDocument;
+
     private PrintJobInfo mCachedInfo;
 
-    PrintJob(PrintJobInfo info, IPrintServiceClient client) {
-        if (client == null) {
-            throw new IllegalStateException("Print serivice not connected!");
-        }
-        mCachedInfo = info;
-        mId = info.getId();
+    PrintJob(PrintJobInfo jobInfo, IPrintServiceClient client) {
+        mCachedInfo = jobInfo;
         mPrintServiceClient = client;
+        mDocument = new PrintDocument(mCachedInfo.getId(), client, jobInfo.getDocumentInfo());
     }
 
     /**
@@ -54,7 +47,7 @@
      * @return The id.
      */
     public int getId() {
-        return mId;
+        return mCachedInfo.getId();
     }
 
     /**
@@ -70,9 +63,9 @@
     public PrintJobInfo getInfo() {
         PrintJobInfo info = null;
         try {
-            info = mPrintServiceClient.getPrintJob(mId);
+            info = mPrintServiceClient.getPrintJobInfo(mCachedInfo.getId());
         } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Couldn't get info for job: " + mId, re);
+            Log.e(LOG_TAG, "Couldn't get info for job: " + mCachedInfo.getId(), re);
         }
         if (info != null) {
             mCachedInfo = info;
@@ -81,6 +74,15 @@
     }
 
     /**
+     * Gets the document of this print job.
+     *
+     * @return The document.
+     */
+    public PrintDocument getDocument() {
+        return mDocument;
+    }
+
+    /**
      * Gets whether this print job is queued. Such a print job is
      * ready to be printed and can be started.
      *
@@ -103,7 +105,7 @@
      * @see #fail(CharSequence)
      */
     public boolean isStarted() {
-        return  getInfo().getState() == PrintJobInfo.STATE_STARTED;
+        return getInfo().getState() == PrintJobInfo.STATE_STARTED;
     }
 
     /**
@@ -181,48 +183,13 @@
      */
     public boolean setTag(String tag) {
         try {
-            return mPrintServiceClient.setPrintJobTag(mId, tag);
+            return mPrintServiceClient.setPrintJobTag(mCachedInfo.getId(), tag);
         } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error setting tag for job:" + mId, re);
+            Log.e(LOG_TAG, "Error setting tag for job: " + mCachedInfo.getId(), re);
         }
         return false;
     }
 
-    /**
-     * Gets the data associated with this print job. It is a responsibility of
-     * the print service to open a stream to the returned file descriptor
-     * and fully read the content.
-     * <p>
-     * <strong>Note:</strong> It is your responsibility to close the file descriptor.
-     * </p>
-     *
-     * @return A file descriptor for reading the data or <code>null</code>.
-     */
-    public final FileDescriptor getData() {
-        ParcelFileDescriptor source = null;
-        ParcelFileDescriptor sink = null;
-        try {
-            ParcelFileDescriptor[] fds = ParcelFileDescriptor.createPipe();
-            source = fds[0];
-            sink = fds[1];
-            mPrintServiceClient.writePrintJobData(sink, mId);
-            return source.getFileDescriptor();
-        } catch (IOException ioe) {
-            Log.e(LOG_TAG, "Error calling getting print job data!", ioe);
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error calling getting print job data!", re);
-        } finally {
-            if (sink != null) {
-                try {
-                    sink.close();
-                } catch (IOException ioe) {
-                    /* ignore */
-                }
-            }
-        }
-        return null;
-    }
-
     @Override
     public boolean equals(Object obj) {
         if (this == obj) {
@@ -235,23 +202,25 @@
             return false;
         }
         PrintJob other = (PrintJob) obj;
-        return (mId == other.mId);
+        return (mCachedInfo.getId() == other.mCachedInfo.getId());
     }
 
     @Override
     public int hashCode() {
-        return mId;
+        return mCachedInfo.getId();
     }
 
     private boolean setState(int 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.
-        mCachedInfo.setState(state);
         try {
-            return mPrintServiceClient.setPrintJobState(mId, state);
+            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);
+                return true;
+            }
         } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error setting the state of job:" + mId, re);
+            Log.e(LOG_TAG, "Error setting the state of job: " + mCachedInfo.getId(), re);
         }
         return false;
     }
diff --git a/core/java/android/printservice/PrintService.java b/core/java/android/printservice/PrintService.java
index 9256966..820c2d8 100644
--- a/core/java/android/printservice/PrintService.java
+++ b/core/java/android/printservice/PrintService.java
@@ -25,6 +25,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
+import android.print.IPrinterDiscoveryObserver;
 import android.print.PrintJobInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
@@ -47,7 +48,7 @@
  * {@link #onStartPrinterDiscovery()} and ends with a call to
  * {@link #onStopPrinterDiscovery()}. During a printer discovery
  * period the print service reports newly discovered printers by
- * calling {@link #addDiscoveredPrinters(List)} and added printers
+ * calling {@link #addDiscoveredPrinters(List)} and reports added printers
  * that disappeared by calling {@link #removeDiscoveredPrinters(List)}.
  * Calls to {@link #addDiscoveredPrinters(List)} and
  * {@link #removeDiscoveredPrinters(List)} before a call to
@@ -67,26 +68,30 @@
  * a call to {@link #onPrintJobQueued(PrintJob)} is made and the print
  * service may handle it immediately or schedule that for an appropriate
  * time in the future. The list of all print jobs for this service
- * are be available by calling {@link #getPrintJobs()}. A queued print
- * job is one whose {@link PrintJob#isQueued()} return true.
+ * are be available by calling {@link #getPrintJobs()}.
  * </p>
  * <p>
  * A print service is responsible for setting the print job state as
  * appropriate while processing it. Initially, a print job is in a
  * {@link PrintJobInfo#STATE_QUEUED} state which means that the data to
  * be printed is spooled by the system and the print service can obtain
- * that data by calling {@link PrintJob#getData()}. After the print
- * service starts printing the data it should set the print job state
- * to {@link PrintJobInfo#STATE_STARTED}. Upon successful completion, the
- * print job state has to be set to {@link PrintJobInfo#STATE_COMPLETED}.
- * In a case of a failure, the print job state should be set to
- * {@link PrintJobInfo#STATE_FAILED}. If a print job is in a
- * {@link PrintJobInfo#STATE_STARTED} state and the user requests to
- * cancel it, the print service will receive a call to
- * {@link #onRequestCancelPrintJob(PrintJob)} which requests from the
- * service to do a best effort in canceling the job. In case the job
- * is successfully canceled, its state has to be set to
- * {@link PrintJobInfo#STATE_CANCELED}.
+ * that data by calling {@link PrintJob#getDocument()}. A queued print
+ * job's {@link PrintJob#isQueued()} method returns true.
+ * </p>
+ * <p>
+ * After the print service starts printing the data it should set the
+ * print job state to {@link PrintJobInfo#STATE_STARTED} by calling
+ * {@link PrintJob#start()}. Upon successful completion, the print job
+ * state has to be set to {@link PrintJobInfo#STATE_COMPLETED} by calling
+ * {@link PrintJob#complete()}. In case of a failure, the print job
+ * state should be set to {@link PrintJobInfo#STATE_FAILED} by calling
+ * {@link PrintJob#fail(CharSequence)}. If a print job is in a
+ * {@link PrintJobInfo#STATE_STARTED} state, i.e. {@link PrintJob#isStarted()}
+ * return true, and the user requests to cancel it, the print service will
+ * receive a call to {@link #onRequestCancelPrintJob(PrintJob)} which
+ * requests from the service to do a best effort in canceling the job. In
+ * case the job is successfully canceled, its state has to be set to
+ * {@link PrintJobInfo#STATE_CANCELED}. by calling {@link PrintJob#cancel()}.
  * </p>
  * <h3>Lifecycle</h3>
  * <p>
@@ -124,9 +129,9 @@
  * <p>
  * A print service can be configured by specifying an optional settings
  * activity which exposes service specific options, an optional add
- * prints activity which is used for manual addition of printers, etc.
- * It is a responsibility of the system to launch the settings and add
- * printers activities when appropriate.
+ * prints activity which is used for manual addition of printers, vendor
+ * name ,etc. It is a responsibility of the system to launch the settings
+ * and add printers activities when appropriate.
  * </p>
  * <p>
  * A print service is configured by providing a
@@ -148,7 +153,7 @@
  */
 public abstract class PrintService extends Service {
 
-    private static final String LOG_TAG = PrintService.class.getSimpleName();
+    private static final String LOG_TAG = "PrintService";
 
     /**
      * The {@link Intent} action that must be declared as handled by a service
@@ -162,6 +167,7 @@
      * <code>&lt;{@link android.R.styleable#PrintService print-service}&gt;</code>
      * tag. This is a a sample XML file configuring a print service:
      * <pre> &lt;print-service
+     *     android:vendor="SomeVendor"
      *     android:settingsActivity="foo.bar.MySettingsActivity"
      *     andorid:addPrintersActivity="foo.bar.MyAddPrintersActivity."
      *     . . .
@@ -175,7 +181,7 @@
 
     private IPrintServiceClient mClient;
 
-    private boolean mDiscoveringPrinters;
+    private IPrinterDiscoveryObserver mDiscoveryObserver;
 
     @Override
     protected void attachBaseContext(Context base) {
@@ -230,29 +236,29 @@
      * printers have to be added. You can call this method as many times as
      * necessary during the discovery period but should not pass in already
      * added printers. If a printer is already added in the same printer
-     * discovery period, it will be ignored.
+     * discovery period, it will be ignored. If you want to update an already
+     * added printer, you should removed it and then re-add it.
      * </p>
      *
      * @param printers A list with discovered printers.
      *
-     * @throws IllegalStateException If this service is not connected.
-     *
      * @see #removeDiscoveredPrinters(List)
      * @see #onStartPrinterDiscovery()
      * @see #onStopPrinterDiscovery()
+     *
+     * @throws IllegalStateException If this service is not connected.
      */
     public final void addDiscoveredPrinters(List<PrinterInfo> printers) {
+        final IPrinterDiscoveryObserver observer;
         synchronized (mLock) {
-            if (mClient == null) {
-                throw new IllegalStateException("Print serivice not connected!");
-            }
-            if (mDiscoveringPrinters) {
-                try {
-                    // Calling with a lock into the system is fine.
-                    mClient.addDiscoveredPrinters(printers);
-                } catch (RemoteException re) {
-                    Log.e(LOG_TAG, "Error adding discovered printers!", re);
-                }
+            throwIfNotConnectedLocked();
+            observer = mDiscoveryObserver;
+        }
+        if (observer != null) {
+            try {
+                observer.addDiscoveredPrinters(printers);
+            } catch (RemoteException re) {
+                Log.e(LOG_TAG, "Error adding discovered printers", re);
             }
         }
     }
@@ -269,37 +275,38 @@
      * this method as many times as necessary during the discovery period
      * but should not pass in already removed printer ids. If a printer with
      * a given id is already removed in the same discovery period, it will
-     * be ignored.
+     * be ignored. If you want to update an already added printer, you should
+     * removed it and then re-add it.
      * </p>
      *
      * @param printerIds A list with disappeared printer ids.
      *
-     * @throws IllegalStateException If this service is not connected.
-     *
      * @see #addDiscoveredPrinters(List)
      * @see #onStartPrinterDiscovery()
      * @see #onStopPrinterDiscovery()
+     *
+     * @throws IllegalStateException If this service is not connected.
      */
     public final void removeDiscoveredPrinters(List<PrinterId> printerIds) {
+        final IPrinterDiscoveryObserver observer;
         synchronized (mLock) {
-            if (mClient == null) {
-                throw new IllegalStateException("Print serivice not connected!");
-            }
-            if (mDiscoveringPrinters) {
-                try {
-                    // Calling with a lock into the system is fine.
-                    mClient.removeDiscoveredPrinters(printerIds);
-                } catch (RemoteException re) {
-                    Log.e(LOG_TAG, "Error removing discovered printers!", re);
-                }
+            throwIfNotConnectedLocked();
+            observer = mDiscoveryObserver;
+        }
+        if (observer != null) {
+            try {
+                observer.removeDiscoveredPrinters(printerIds);
+            } catch (RemoteException re) {
+                Log.e(LOG_TAG, "Error removing discovered printers", re);
             }
         }
     }
 
     /**
      * Called when canceling of a print job is requested. The service
-     * should do best effort to fulfill the request. After the print
-     * job is canceled by calling {@link PrintJob#cancel()}.
+     * should do best effort to fulfill the request. After the cancellation
+     * is performed, the print job should be set to a cancelled state by
+     * calling {@link PrintJob#cancel()}.
      *
      * @param printJob The print job to be canceled.
      */
@@ -310,11 +317,12 @@
      * Called when there is a queued print job for one of the printers
      * managed by this print service. A queued print job is ready for
      * processing by a print service which can get the data to be printed
-     * by calling {@link PrintJob#getData()}. This service may start
+     * by calling {@link PrintJob#getDocument()}. This service may start
      * processing the passed in print job or schedule handling of queued
      * print jobs at a convenient time. The service can get the print
      * jobs by a call to {@link #getPrintJobs()} and examine their state
-     * to find the ones with state {@link PrintJobInfo#STATE_QUEUED}.
+     * to find the ones with state {@link PrintJobInfo#STATE_QUEUED} by
+     * calling {@link PrintJob#isQueued()}.
      *
      * @param printJob The new queued print job.
      *
@@ -330,28 +338,31 @@
      * @throws IllegalStateException If this service is not connected.
      */
     public final List<PrintJob> getPrintJobs() {
+        final IPrintServiceClient client;
         synchronized (mLock) {
-            if (mClient == null) {
-                throw new IllegalStateException("Print serivice not connected!");
-            }
-            try {
-                List<PrintJob> printJobs = null;
-                List<PrintJobInfo> printJobInfos = mClient.getPrintJobs();
-                if (printJobInfos != null) {
-                    final int printJobInfoCount = printJobInfos.size();
-                    printJobs = new ArrayList<PrintJob>(printJobInfoCount);
-                    for (int i = 0; i < printJobInfoCount; i++) {
-                        printJobs.add(new PrintJob(printJobInfos.get(i), mClient));
-                    }
-                }
-                if (printJobs != null) {
-                    return printJobs;
-                }
-            } catch (RemoteException re) {
-                Log.e(LOG_TAG, "Error calling getPrintJobs()", re);
-            }
+            throwIfNotConnectedLocked();
+            client = mClient;
+        }
+        if (client == null) {
             return Collections.emptyList();
         }
+        try {
+            List<PrintJob> printJobs = null;
+            List<PrintJobInfo> printJobInfos = client.getPrintJobInfos();
+            if (printJobInfos != null) {
+                final int printJobInfoCount = printJobInfos.size();
+                printJobs = new ArrayList<PrintJob>(printJobInfoCount);
+                for (int i = 0; i < printJobInfoCount; i++) {
+                    printJobs.add(new PrintJob(printJobInfos.get(i), client));
+                }
+            }
+            if (printJobs != null) {
+                return printJobs;
+            }
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error calling getPrintJobs()", re);
+        }
+        return Collections.emptyList();
     }
 
     /**
@@ -375,8 +386,9 @@
             }
 
             @Override
-            public void startPrinterDiscovery() {
-                mHandler.sendEmptyMessage(MyHandler.MESSAGE_START_PRINTER_DISCOVERY);
+            public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+                mHandler.obtainMessage(MyHandler.MESSAGE_START_PRINTER_DISCOVERY,
+                        observer).sendToTarget();
             }
 
             @Override
@@ -385,18 +397,25 @@
             }
 
             @Override
-            public void requestCancelPrintJob(PrintJobInfo printJob) {
-                mHandler.obtainMessage(MyHandler.MESSAGE_CANCEL_PRINTJOB, printJob).sendToTarget();
+            public void requestCancelPrintJob(PrintJobInfo printJobInfo) {
+                mHandler.obtainMessage(MyHandler.MESSAGE_CANCEL_PRINTJOB,
+                        printJobInfo).sendToTarget();
             }
 
             @Override
-            public void onPrintJobQueued(PrintJobInfo printJob) {
+            public void onPrintJobQueued(PrintJobInfo printJobInfo) {
                 mHandler.obtainMessage(MyHandler.MESSAGE_ON_PRINTJOB_QUEUED,
-                        printJob).sendToTarget();
+                        printJobInfo).sendToTarget();
             }
         };
     }
 
+    private void throwIfNotConnectedLocked() {
+        if (mClient == null) {
+            throw new IllegalStateException("Print serivice not connected");
+        }
+    }
+
     private final class MyHandler extends Handler {
         public static final int MESSAGE_START_PRINTER_DISCOVERY = 1;
         public static final int MESSAGE_STOP_PRINTER_DISCOVERY = 2;
@@ -414,26 +433,26 @@
             switch (action) {
                 case MESSAGE_START_PRINTER_DISCOVERY: {
                     synchronized (mLock) {
-                        mDiscoveringPrinters = true;
+                        mDiscoveryObserver = (IPrinterDiscoveryObserver) message.obj;
                     }
                     onStartPrinterDiscovery();
                 } break;
 
                 case MESSAGE_STOP_PRINTER_DISCOVERY: {
                     synchronized (mLock) {
-                        mDiscoveringPrinters = false;
+                        mDiscoveryObserver = null;
                     }
                     onStopPrinterDiscovery();
                 } break;
 
                 case MESSAGE_CANCEL_PRINTJOB: {
-                    PrintJobInfo printJob = (PrintJobInfo) message.obj;
-                    onRequestCancelPrintJob(new PrintJob(printJob, mClient));
+                    PrintJobInfo printJobInfo = (PrintJobInfo) message.obj;
+                    onRequestCancelPrintJob(new PrintJob(printJobInfo, mClient));
                 } break;
 
                 case MESSAGE_ON_PRINTJOB_QUEUED: {
-                    PrintJobInfo printJob = (PrintJobInfo) message.obj;
-                    onPrintJobQueued(new PrintJob(printJob, mClient));
+                    PrintJobInfo printJobInfo = (PrintJobInfo) message.obj;
+                    onPrintJobQueued(new PrintJob(printJobInfo, mClient));
                 } break;
 
                 case MESSAGE_SET_CLEINT: {
@@ -441,13 +460,12 @@
                     synchronized (mLock) {
                         mClient = client;
                         if (client == null) {
-                            mDiscoveringPrinters = false;
+                            mDiscoveryObserver = null;
                         }
                     }
                     if (client != null) {
                         onConnected();
                      } else {
-                        onStopPrinterDiscovery();
                         onDisconnected();
                     }
                 } break;
diff --git a/core/java/android/printservice/PrintServiceInfo.java b/core/java/android/printservice/PrintServiceInfo.java
index 0370a25..43dd1b6 100644
--- a/core/java/android/printservice/PrintServiceInfo.java
+++ b/core/java/android/printservice/PrintServiceInfo.java
@@ -48,8 +48,6 @@
  */
 public final class PrintServiceInfo implements Parcelable {
 
-    private static final boolean DEBUG = false;
-
     private static final String LOG_TAG = PrintServiceInfo.class.getSimpleName();
 
     private static final String TAG_PRINT_SERVICE = "print-service";
@@ -97,7 +95,6 @@
      * @param context Context for accessing resources.
      * @throws XmlPullParserException If a XML parsing error occurs.
      * @throws IOException If a I/O error occurs.
-     * @hide
      */
     public static PrintServiceInfo create(ResolveInfo resolveInfo, Context context) {
         String settingsActivityName = null;
@@ -117,7 +114,7 @@
                 String nodeName = parser.getName();
                 if (!TAG_PRINT_SERVICE.equals(nodeName)) {
                     throw new XmlPullParserException(
-                            "Meta-data does not start with" + TAG_PRINT_SERVICE + " tag");
+                            "Meta-data does not start with " + TAG_PRINT_SERVICE + " tag");
                 }
 
                 Resources resources = packageManager.getResourcesForApplication(
@@ -213,7 +210,7 @@
 
     @Override
     public int hashCode() {
-        return 31 * 1 + ((mId == null) ? 0 : mId.hashCode());
+        return 31 + ((mId == null) ? 0 : mId.hashCode());
     }
 
     @Override
@@ -244,12 +241,8 @@
         builder.append("PrintServiceInfo{");
         builder.append("id:").append(mId).append(", ");
         builder.append("resolveInfo:").append(mResolveInfo).append(", ");
-        if (DEBUG) {
-            builder.append("settingsActivityName:").append(mSettingsActivityName);
-            builder.append("addPrintersActivityName:").append(mAddPrintersActivityName);
-        } else if (mSettingsActivityName != null || mAddPrintersActivityName != null) {
-            builder.append("<has meta-data>");
-        }
+        builder.append("settingsActivityName:").append(mSettingsActivityName);
+        builder.append("addPrintersActivityName:").append(mAddPrintersActivityName);
         builder.append("}");
         return builder.toString();
     }
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 4adee14..d2d1f1b 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -621,7 +621,7 @@
 
     @Override
     public void concat(Matrix matrix) {
-        nConcatMatrix(mRenderer, matrix.native_instance);
+        if (matrix != null) nConcatMatrix(mRenderer, matrix.native_instance);
     }
     
     private static native void nConcatMatrix(int renderer, int matrix);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7447d1b..1c0e73d 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1122,7 +1122,6 @@
      *
      * @see android.graphics.drawable.Drawable
      * @see #getDrawableState()
-     * @hide
      */
     protected static final int[] PRESSED_STATE_SET;
     /**
diff --git a/core/java/android/view/transition/Fade.java b/core/java/android/view/transition/Fade.java
index c2aa90b..4fd60c1 100644
--- a/core/java/android/view/transition/Fade.java
+++ b/core/java/android/view/transition/Fade.java
@@ -32,6 +32,8 @@
  */
 public class Fade extends Visibility {
 
+    private static boolean DBG = Transition.DBG && false;
+
     private static final String LOG_TAG = "Fade";
     private static final String PROPNAME_SCREEN_X = "android:fade:screenX";
     private static final String PROPNAME_SCREEN_Y = "android:fade:screenY";
@@ -121,7 +123,7 @@
         View view;
         View startView = (startValues != null) ? startValues.view : null;
         View endView = (endValues != null) ? endValues.view : null;
-        if (Transition.DBG) {
+        if (DBG) {
             Log.d(LOG_TAG, "Fade.predisappear: startView, startVis, endView, endVis = " +
                         startView + ", " + startVisibility + ", " + endView + ", " + endVisibility);
         }
diff --git a/core/java/android/view/transition/Transition.java b/core/java/android/view/transition/Transition.java
index 6d5e61a..f99ddc0 100644
--- a/core/java/android/view/transition/Transition.java
+++ b/core/java/android/view/transition/Transition.java
@@ -20,6 +20,7 @@
 import android.animation.AnimatorListenerAdapter;
 import android.animation.TimeInterpolator;
 import android.util.ArrayMap;
+import android.util.Log;
 import android.util.LongSparseArray;
 import android.util.Pair;
 import android.util.SparseArray;
@@ -84,11 +85,14 @@
     int mNumInstances = 0;
 
 
-    /**
-     * The set of listeners to be sent transition lifecycle events.
-     */
+
+    // The set of listeners to be sent transition lifecycle events.
     ArrayList<TransitionListener> mListeners = null;
 
+    // The set of animators collected from calls to play(), to be run in runAnimations()
+    ArrayMap<Pair<TransitionValues, TransitionValues>, Animator> mAnimatorMap =
+            new ArrayMap<Pair<TransitionValues, TransitionValues>, Animator>();
+
     /**
      * Constructs a Transition object with no target objects. A transition with
      * no targets defaults to running on all target objects in the scene hierarchy
@@ -203,6 +207,9 @@
      */
     protected void play(ViewGroup sceneRoot, TransitionValuesMaps startValues,
             TransitionValuesMaps endValues) {
+        if (DBG) {
+            Log.d(LOG_TAG, "play() for " + this);
+        }
         mPlayStartValuesList.clear();
         mPlayEndValuesList.clear();
         ArrayMap<View, TransitionValues> endCopy =
@@ -312,20 +319,45 @@
         for (int i = 0; i < startValuesList.size(); ++i) {
             TransitionValues start = startValuesList.get(i);
             TransitionValues end = endValuesList.get(i);
-            // TODO: what to do about targetIds and itemIds?
-            Animator animator = play(sceneRoot, start, end);
-            if (animator != null) {
-                mAnimatorMap.put(new Pair(start, end), animator);
-                // Note: we've already done the check against targetIDs in these lists
-                mPlayStartValuesList.add(start);
-                mPlayEndValuesList.add(end);
+            // Only bother trying to animate with values that differ between start/end
+            if (start != null || end != null) {
+                if (start == null || !start.equals(end)) {
+                    if (DBG) {
+                        View view = (end != null) ? end.view : start.view;
+                        Log.d(LOG_TAG, "  differing start/end values for view " +
+                                view);
+                        if (start == null || end == null) {
+                            if (start == null) {
+                                Log.d(LOG_TAG, "    " + ((start == null) ?
+                                        "start null, end non-null" : "start non-null, end null"));
+                            }
+                        } else {
+                            for (String key : start.values.keySet()) {
+                                Object startValue = start.values.get(key);
+                                Object endValue = end.values.get(key);
+                                if (startValue != endValue && !startValue.equals(endValue)) {
+                                    Log.d(LOG_TAG, "    " + key + ": start(" + startValue +
+                                            "), end(" + endValue +")");
+                                }
+                            }
+                        }
+                    }
+                    // TODO: what to do about targetIds and itemIds?
+                    Animator animator = play(sceneRoot, start, end);
+                    if (animator != null) {
+                        mAnimatorMap.put(new Pair(start, end), animator);
+                        // Note: we've already done the check against targetIDs in these lists
+                        mPlayStartValuesList.add(start);
+                        mPlayEndValuesList.add(end);
+                    }
+                } else if (DBG) {
+                    View view = (end != null) ? end.view : start.view;
+                    Log.d(LOG_TAG, "  No change for view " + view);
+                }
             }
         }
     }
 
-    ArrayMap<Pair<TransitionValues, TransitionValues>, Animator> mAnimatorMap =
-            new ArrayMap<Pair<TransitionValues, TransitionValues>, Animator>();
-
     /**
      * Internal utility method for checking whether a given view/id
      * is valid for this transition, where "valid" means that either
@@ -364,14 +396,20 @@
      * @hide
      */
     protected void runAnimations() {
-
+        if (DBG && mPlayStartValuesList.size() > 0) {
+            Log.d(LOG_TAG, "runAnimations (" + mPlayStartValuesList.size() + ") on " + this);
+        }
         startTransition();
         // Now walk the list of TransitionValues, calling play for each pair
         for (int i = 0; i < mPlayStartValuesList.size(); ++i) {
             TransitionValues start = mPlayStartValuesList.get(i);
             TransitionValues end = mPlayEndValuesList.get(i);
+            Animator anim = mAnimatorMap.get(new Pair(start, end));
+            if (DBG) {
+                Log.d(LOG_TAG, "  anim: " + anim);
+            }
             startTransition();
-            runAnimator(mAnimatorMap.get(new Pair(start, end)));
+            runAnimator(anim);
         }
         mPlayStartValuesList.clear();
         mPlayEndValuesList.clear();
@@ -871,27 +909,35 @@
     String toString(String indent) {
         String result = indent + getClass().getSimpleName() + "@" +
                 Integer.toHexString(hashCode()) + ": ";
-        result += "dur(" + mDuration + ") ";
-        result += "dly(" + mStartDelay + ") ";
-        result += "interp(" + mInterpolator + ") ";
-        result += "tgts(";
-        if (mTargetIds != null) {
-            for (int i = 0; i < mTargetIds.length; ++i) {
-                if (i > 0) {
-                    result += ", ";
-                }
-                result += mTargetIds[i];
-            }
+        if (mDuration != -1) {
+            result += "dur(" + mDuration + ") ";
         }
-        if (mTargets != null) {
-            for (int i = 0; i < mTargets.length; ++i) {
-                if (i > 0) {
-                    result += ", ";
-                }
-                result += mTargets[i];
-            }
+        if (mStartDelay != -1) {
+            result += "dly(" + mStartDelay + ") ";
         }
-        result += ")";
+        if (mInterpolator != null) {
+            result += "interp(" + mInterpolator + ") ";
+        }
+        if (mTargetIds != null || mTargets != null) {
+            result += "tgts(";
+            if (mTargetIds != null) {
+                for (int i = 0; i < mTargetIds.length; ++i) {
+                    if (i > 0) {
+                        result += ", ";
+                    }
+                    result += mTargetIds[i];
+                }
+            }
+            if (mTargets != null) {
+                for (int i = 0; i < mTargets.length; ++i) {
+                    if (i > 0) {
+                        result += ", ";
+                    }
+                    result += mTargets[i];
+                }
+            }
+            result += ")";
+        }
         return result;
     }
 
diff --git a/core/java/android/view/transition/TransitionManager.java b/core/java/android/view/transition/TransitionManager.java
index b200a6d..7a3d9e2 100644
--- a/core/java/android/view/transition/TransitionManager.java
+++ b/core/java/android/view/transition/TransitionManager.java
@@ -17,6 +17,7 @@
 package android.view.transition;
 
 import android.util.ArrayMap;
+import android.util.Log;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
 
@@ -36,6 +37,8 @@
 public class TransitionManager {
     // TODO: how to handle enter/exit?
 
+    private static String LOG_TAG = "TransitionManager";
+
     private static final Transition sDefaultTransition = new AutoTransition();
     private Transition mDefaultTransition = new AutoTransition();
 
@@ -164,6 +167,7 @@
             observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                 public boolean onPreDraw() {
                     sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
+                    sPendingTransitions.remove(sceneRoot);
                     // Add to running list, handle end to remove it
                     sRunningTransitions.put(sceneRoot, transition);
                     transition.addListener(new Transition.TransitionListenerAdapter() {
@@ -316,8 +320,11 @@
      * value of null causes the TransitionManager to use the default transition.
      */
     public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) {
-
-        if (!sPendingTransitions.contains(sceneRoot)) {
+        if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.hasLayout()) {
+            if (Transition.DBG) {
+                Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " +
+                        sceneRoot + ", " + transition);
+            }
             sPendingTransitions.add(sceneRoot);
             if (transition == null) {
                 transition = sDefaultTransition;
@@ -325,13 +332,7 @@
             final Transition finalTransition = transition.clone();
             sceneChangeSetup(sceneRoot, transition);
             sceneRoot.setCurrentScene(null);
-            sceneRoot.postOnAnimation(new Runnable() {
-                @Override
-                public void run() {
-                    sPendingTransitions.remove(sceneRoot);
-                    sceneChangeRunTransition(sceneRoot, finalTransition);
-                }
-            });
+            sceneChangeRunTransition(sceneRoot, finalTransition);
         }
     }
 }
diff --git a/core/java/android/view/transition/TransitionValues.java b/core/java/android/view/transition/TransitionValues.java
index f361666..6e5d3d3 100644
--- a/core/java/android/view/transition/TransitionValues.java
+++ b/core/java/android/view/transition/TransitionValues.java
@@ -53,6 +53,23 @@
     public final Map<String, Object> values = new ArrayMap<String, Object>();
 
     @Override
+    public boolean equals(Object other) {
+        if (other instanceof TransitionValues) {
+            if (view == ((TransitionValues) other).view) {
+                if (values.equals(((TransitionValues) other).values)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode() {
+        return 31*view.hashCode() + values.hashCode();
+    }
+
+    @Override
     public String toString() {
         String returnValue = "TransitionValues@" + Integer.toHexString(hashCode()) + ":\n";
         returnValue += "    view = " + view + "\n";
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 8b51bf3..52433a5 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1207,15 +1207,13 @@
      */
     public void setFastScrollEnabled(boolean enabled) {
         mFastScrollEnabled = enabled;
-        if (enabled) {
-            if (mFastScroller == null) {
-                mFastScroller = new FastScroller(getContext(), this);
-            }
-        } else {
-            if (mFastScroller != null) {
-                mFastScroller.stop();
-                mFastScroller = null;
-            }
+
+        if (enabled && mFastScroller == null) {
+            mFastScroller = new FastScroller(getContext(), this);
+        }
+
+        if (mFastScroller != null) {
+            mFastScroller.setEnabled(enabled);
         }
     }
 
diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java
index 594b130..f3c0687 100644
--- a/core/java/android/widget/FastScroller.java
+++ b/core/java/android/widget/FastScroller.java
@@ -175,6 +175,9 @@
     /** Whether decorations should be laid out from right to left. */
     private boolean mLayoutFromRight;
 
+    /** Whether the fast scroller is enabled. */
+    private boolean mEnabled;
+
     /** Whether the scrollbar and decorations should always be shown. */
     private boolean mAlwaysShow;
 
@@ -302,6 +305,39 @@
     }
 
     /**
+     * Removes this FastScroller overlay from the host view.
+     */
+    public void remove() {
+        mOverlay.remove(mTrackImage);
+        mOverlay.remove(mThumbImage);
+        mOverlay.remove(mPreviewImage);
+        mOverlay.remove(mPrimaryText);
+        mOverlay.remove(mSecondaryText);
+    }
+
+    /**
+     * @param enabled Whether the fast scroll thumb is enabled.
+     */
+    public void setEnabled(boolean enabled) {
+        mEnabled = enabled;
+
+        if (enabled) {
+            if (mAlwaysShow) {
+                setState(STATE_VISIBLE);
+            }
+        } else {
+            stop();
+        }
+    }
+
+    /**
+     * @return Whether the fast scroll thumb is enabled.
+     */
+    public boolean isEnabled() {
+        return mEnabled;
+    }
+
+    /**
      * @param alwaysShow Whether the fast scroll thumb should always be shown
      */
     public void setAlwaysShow(boolean alwaysShow) {
@@ -329,18 +365,6 @@
         setState(STATE_NONE);
     }
 
-    /**
-     * @return Whether the fast scroll thumb should be shown.
-     */
-    public boolean shouldShow() {
-        // Don't show if the list is as tall as or shorter than the thumbnail.
-        if (mList.getHeight() <= mThumbImage.getHeight()) {
-            return false;
-        }
-
-        return true;
-    }
-
     public void setScrollbarPosition(int position) {
         if (position == View.SCROLLBAR_POSITION_DEFAULT) {
             position = mList.isLayoutRtl() ?
@@ -696,7 +720,7 @@
     }
 
     public void onScroll(int firstVisibleItem, int visibleItemCount, int totalItemCount) {
-        if (!mAlwaysShow && !isLongList(visibleItemCount, totalItemCount)) {
+        if (!mEnabled || !mAlwaysShow && !isLongList(visibleItemCount, totalItemCount)) {
             setState(STATE_NONE);
             return;
         }
@@ -1106,6 +1130,10 @@
     }
 
     public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (!mEnabled) {
+            return false;
+        }
+
         switch (ev.getActionMasked()) {
             case MotionEvent.ACTION_DOWN:
                 if (isPointInside(ev.getX(), ev.getY())) {
@@ -1134,6 +1162,10 @@
     }
 
     public boolean onTouchEvent(MotionEvent me) {
+        if (!mEnabled) {
+            return false;
+        }
+
         switch (me.getActionMasked()) {
             case MotionEvent.ACTION_DOWN: {
                 if (isPointInside(me.getX(), me.getY())) {
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 3d6b116..4d0a326 100644
--- a/core/java/com/android/internal/view/menu/MenuItemImpl.java
+++ b/core/java/com/android/internal/view/menu/MenuItemImpl.java
@@ -616,7 +616,7 @@
 
     @Override
     public boolean expandActionView() {
-        if (hasCollapsibleActionView()) {
+        if (!hasCollapsibleActionView()) {
             return false;
         }
 
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 753f5dc..3600b76 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -200,7 +200,8 @@
 static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jint texName,
         jobject weakThiz, jboolean allowSynchronous)
 {
-    sp<GLConsumer> surfaceTexture(new GLConsumer(texName, allowSynchronous));
+    sp<BufferQueue> bq = new BufferQueue(allowSynchronous);
+    sp<GLConsumer> surfaceTexture(new GLConsumer(bq, texName));
     if (surfaceTexture == 0) {
         jniThrowException(env, OutOfResourcesException,
                 "Unable to create native SurfaceTexture");
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 1a73f42..942d33f 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -520,6 +520,10 @@
     // regular write() or copy the data to the AudioTrack's shared memory?
     if (track->sharedBuffer() == 0) {
         written = track->write(data + offsetInBytes, sizeInBytes);
+        // for compatibility with earlier behavior of write(), return 0 in this case
+        if (written == (ssize_t) WOULD_BLOCK) {
+            written = 0;
+        }
     } else {
         if (audioFormat == javaAudioTrackFields.PCM16) {
             // writing to shared memory, check for capacity
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 8b7f3dd..61ace4a 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -380,10 +380,11 @@
     android_os_Debug_getDirtyPagesPid(env, clazz, getpid(), object);
 }
 
-static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid)
+static jlong android_os_Debug_getPssPid(JNIEnv *env, jobject clazz, jint pid, jlongArray outUss)
 {
     char line[1024];
     jlong pss = 0;
+    jlong uss = 0;
     unsigned temp;
 
     char tmp[128];
@@ -398,23 +399,42 @@
             break;
         }
 
-        if (strncmp(line, "Pss: ", 5) == 0) {
-            char* c = line + 5;
-            while (*c != 0 && (*c < '0' || *c > '9')) {
-                c++;
+        if (line[0] == 'P') {
+            if (strncmp(line, "Pss:", 4) == 0) {
+                char* c = line + 4;
+                while (*c != 0 && (*c < '0' || *c > '9')) {
+                    c++;
+                }
+                pss += atoi(c);
+            } else if (strncmp(line, "Private_Clean:", 14)
+                    || strncmp(line, "Private_Dirty:", 14)) {
+                char* c = line + 14;
+                while (*c != 0 && (*c < '0' || *c > '9')) {
+                    c++;
+                }
+                uss += atoi(c);
             }
-            pss += atoi(c);
         }
     }
 
     fclose(fp);
 
+    if (outUss != NULL) {
+        if (env->GetArrayLength(outUss) >= 1) {
+            jlong* outUssArray = env->GetLongArrayElements(outUss, 0);
+            if (outUssArray != NULL) {
+                outUssArray[0] = uss;
+            }
+            env->ReleaseLongArrayElements(outUss, outUssArray, 0);
+        }
+    }
+
     return pss;
 }
 
 static jlong android_os_Debug_getPss(JNIEnv *env, jobject clazz)
 {
-    return android_os_Debug_getPssPid(env, clazz, getpid());
+    return android_os_Debug_getPssPid(env, clazz, getpid(), NULL);
 }
 
 static jint read_binder_stat(const char* stat)
@@ -689,7 +709,7 @@
             (void*) android_os_Debug_getDirtyPagesPid },
     { "getPss",                 "()J",
             (void*) android_os_Debug_getPss },
-    { "getPss",                 "(I)J",
+    { "getPss",                 "(I[J)J",
             (void*) android_os_Debug_getPssPid },
     { "dumpNativeHeap",         "(Ljava/io/FileDescriptor;)V",
             (void*) android_os_Debug_dumpNativeHeap },
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 33ded03..6d97d01 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -33,6 +33,7 @@
 #include <sys/errno.h>
 #include <sys/resource.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <dirent.h>
 #include <fcntl.h>
 #include <grp.h>
@@ -381,6 +382,32 @@
     return false;
 }
 
+jboolean android_os_Process_setSwappiness(JNIEnv *env, jobject clazz,
+                                          jint pid, jboolean is_increased)
+{
+    char text[64];
+
+    if (is_increased) {
+        strcpy(text, "/sys/fs/cgroup/memory/sw/tasks");
+    } else {
+        strcpy(text, "/sys/fs/cgroup/memory/tasks");
+    }
+
+    struct stat st;
+    if (stat(text, &st) || !S_ISREG(st.st_mode)) {
+        return false;
+    }
+
+    int fd = open(text, O_WRONLY);
+    if (fd >= 0) {
+        sprintf(text, "%d", pid);
+        write(fd, text, strlen(text));
+        close(fd);
+    }
+
+    return true;
+}
+
 void android_os_Process_setArgV0(JNIEnv* env, jobject clazz, jstring name)
 {
     if (name == NULL) {
@@ -1022,6 +1049,7 @@
     {"setProcessGroup",     "(II)V", (void*)android_os_Process_setProcessGroup},
     {"getProcessGroup",     "(I)I", (void*)android_os_Process_getProcessGroup},
     {"setOomAdj",   "(II)Z", (void*)android_os_Process_setOomAdj},
+    {"setSwappiness",   "(IZ)Z", (void*)android_os_Process_setSwappiness},
     {"setArgV0",    "(Ljava/lang/String;)V", (void*)android_os_Process_setArgV0},
     {"setUid", "(I)I", (void*)android_os_Process_setUid},
     {"setGid", "(I)I", (void*)android_os_Process_setGid},
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 06a5e67..0d206d3 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Laat die program toe om netwerkbeleide te bestuur en program-spesifieke reëls te definieer."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"verander verrekening van netwerkgebruik"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Laat die program toe om te verander hoe netwerkgebruik teenoor programme gemeet word. Nie vir gebruik deur normale programme nie."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"kry toegang tot kennisgewings"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Laat die program toe om kennisgewings op te haal, te bestudeer en te verwyder, insluitende die kennisgewings wat deur ander programme geplaas is."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bind aan \'n kennisgewingluisteraardiens"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 3796f73..be512f6 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"የአውታረመረብ ቋሚ መመሪያዎችን እና ትግበራ ተኮር ደንቦችን ለማደራጀት ለመተግበሪያው ይፈቅዳሉ፡፡"</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"የአውታረ መረብ አጠቃቀም"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"ከመተግበሪያዎች በተለየ መልኩ እንዴት የአውታረ መረብ አጠቃቀም እንደተመዘገበ ለመቀየር ለመተግበሪያው ይፈቅዳሉ።ለመደበኛ መተግበሪያዎች አገልግሎት አይውልም።"</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"ማሳወቂያዎችን ይድረሱ"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"መተግበሪያው ማሳወቂያዎችን እንዲያስመጣ፣ እንዲመረምር እና እንዲያጸዳ ያስችለዋል፣ በሌሎች መተግበሪያዎች የተለጠፉትንም ጨምሮ።"</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"ከአንድ የማሳወቂያ አዳማጭ አገልግሎት ጋር ይሰሩ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 1820689..39320d7 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"للسماح لتطبيق بإدارة سياسات الشبكة وتحديد قواعد متعلقة بالتطبيق."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"تعديل حساب استخدام الشبكة"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"للسماح للتطبيق بتعديل كيفية حساب استخدام الشبكة في التطبيقات. ليس للاستخدام بواسطة التطبيقات العادية."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"إشعارات الدخول"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"يتيح للتطبيق استرجاع الإشعارات وفحصها ومسحها، بما في ذلك تلك التي نشرتها تطبيقات أخرى."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"الربط بخدمة تلقّي الإشعارات الصوتية"</string>
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index becac6c..e27ba07 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Дазваляе прыкладаннм кіраваць сеткавымі палітыкамі і вызначаць правілы пэўных прыкладанняў."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"змяніць улік выкарыстання сеткі"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Дазваляе прыкладанням змяняць метад уліку выкарыстання сеткі прыкладаннямі. Не для выкарыстання звычайнымі прыкладаннямі."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"доступ да паведамленняў"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Дазваляе прыкладанню атрымлiваць, правяраць i выдаляць апавяшчэннi, у тым лiку апублiкаваныя iншымi прыкладаннямi."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"прывязка да службы апавяшчэння слухача"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index a4fa7f0..2a1c01d 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Разрешава на приложението да управлява правилата на мрежата и да определя такива за конкретно приложение."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"промяна на отчетността на употребата на мрежа"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Разрешава на приложението да променя това как употребата на мрежа се отчита спрямо приложенията. Не е предназначено за нормални приложения."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"достъп до известията"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Разрешава на приложението да извлича, преглежда и изчиства известия, включително публикуваните от други приложения."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"обвързване с услуга за слушател на известия"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 05965b3..8348e0f 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permet que l\'aplicació gestioni les polítiques de la xarxa i que defineixi les regles específiques d\'aplicació."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modificació del càlcul d\'ús de la xarxa"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permet que l\'aplicació modifiqui la manera com es calcula l\'ús de la xarxa per part de les aplicacions. No indicat per a les aplicacions normals."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"accedeix a les notificacions"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permet que l\'aplicació recuperi, examini i esborri les notificacions, incloses les que han publicat altres aplicacions."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincula a un servei de processament de notificacions"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 9f3a090..836715e 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Umožňuje aplikaci spravovat zásady sítě a definovat pravidla pro konkrétní aplikace."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"upravit kontrolu používání sítě"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Umožňuje aplikaci upravit způsob výpočtu využití sítě aplikacemi. Toto oprávnění není určeno pro běžné aplikace."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"přístup k oznámením"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Umožňuje aplikacím načítat, zobrazovat a mazat oznámení včetně těch přidaných jinými aplikacemi."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"navázání na službu pro poslouchání oznámení"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 80e14c8..9964272 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Tillader, at appen kan administrere netværkspolitikker og definere appspecifikke regler."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"skift afregning af netværksbrug"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Tillader, at appen kan ændre den måde, som netværksforbrug udregnes på i forhold til apps. Anvendes ikke af normale apps."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"adgang til underretninger"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillader, at appen kan hente, undersøge og rydde underretninger, herunder dem, der er sendt af andre apps."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"forpligte sig til en underretningslyttertjeneste"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index be81e63..fad1558 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Ermöglicht der App, Netzwerkrichtlinien zu verwalten und anwendungsspezifische Regeln festzulegen"</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"Zuordnung für Netzwerknutzung ändern"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Ermöglicht der App, die Art und Weise zu ändern, wie der Netzwerkverbrauch im Hinblick auf Apps berechnet wird. Nicht für normale Apps vorgesehen."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"Auf Benachrichtigungen zugreifen"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Ermöglicht der App das Abrufen, Überprüfen und Löschen von Benachrichtigungen, einschließlich Benachrichtigungen, die von anderen Apps gepostet wurden"</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"An Benachrichtigungs-Listener-Dienst binden"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 961e15e..07df59f 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Επιτρέπει στην εφαρμογή τη διαχείριση των πολιτικών δικτύου και τον ορισμό κανόνων για ορισμένες εφαρμογές."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"τροποποίηση υπολογισμού χρήσης δικτύου"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Επιτρέπει στην εφαρμογή την τροποποίηση του τρόπου υπολογισμού της χρήσης δικτύου έναντι των εφαρμογών. Δεν προορίζεται για χρήση από συνήθεις εφαρμογές."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"πρόσβαση στις ειδοποιήσεις"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Επιτρέπει στην εφαρμογή να ανακτά, να εξετάζει και να απαλείφει ειδοποιήσεις, συμπεριλαμβανομένων εκείνων που δημοσιεύονται από άλλες εφαρμογές."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"δέσμευση σε υπηρεσία ακρόασης ειδοποίησης"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 4825085..ac1ef54 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Allows the app to manage network policies and define app-specific rules."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modify network usage accounting"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Allows the app to modify how network usage is accounted against apps. Not for use by normal apps."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"access notifications"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Allows the app to retrieve, examine, and clear notifications, including those posted by other apps."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bind to a notification listener service"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 615fcbe..4a4dfd0 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permite que la aplicación administre las políticas de red y defina reglas específicas de la aplicación."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"Modificar la administración del uso de redes"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que la aplicación modifique cómo se registra el uso de red en relación con las aplicaciones. Las aplicaciones normales no deben usar este permiso."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"acceder a las notificaciones"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que la aplicación recupere, examine y elimine notificaciones, incluidas aquellas publicadas por otras aplicaciones."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"Vincular a un servicio de agente de escucha de notificaciones"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 2dbe795..5069333 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permite que la aplicación administre políticas de red y defina reglas específicas de la aplicación."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modificar cálculo de uso de red"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que la aplicación modifique cómo se registra el uso de red en relación con las aplicaciones. Las aplicaciones normales no deben usar este permiso."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"acceder a las notificaciones"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que la aplicación recupere, examine y borre notificaciones, incluidas las que han publicado otras aplicaciones."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"enlazar con un servicio de detector de notificaciones"</string>
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index cd3d97c..1a2bc60 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Võimaldab rakendusel hallata võrgueeskirju ja määratleda rakendusespetsiifilisi reegleid."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"võrgukasutuse arvestamise muutmine"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Võimaldab rakendusel muuta võrgukasutuse loendamist rakenduste suhtes. Mitte kasutada tavarakenduste puhul."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"juurdepääsu märguanded"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Võimaldab rakendusel tuua, kontrollida ja kustutada märguandeid, sh neid, mille on postitanud teised rakendused."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"seo märguannete kuulamisteenusega"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index d4b1cb7..697450d 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"به برنامه اجازه می‎دهد تا خط مشی‎های شبکه را مدیریت کند و قوانین خاص برنامه را تعیین کند."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"اصلاح محاسبه استفاده از شبکه"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"به برنامه اجازه می‎دهد تا نحوه محاسبه کاربرد شبکه در برنامه را تغییر دهد. برای استفاده برنامه‎های عادی نیست."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"اعلان‌های دسترسی"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"به برنامه اجازه می‌دهد به بازیابی، بررسی و پاک کردن اعلان‌ها از جمله موارد پست شده توسط سایر برنامه‌ها بپردازد."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"اتصال به یک سرویس شنونده اعلان"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index bded5cc..34c5eff 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Sallii sovelluksen hallinnoida verkkokäytäntöjä ja määritellä sovelluskohtaisia sääntöjä."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"verkon käytön seurannan muokkaaminen"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Antaa sovelluksen muokata, miten sovellusten verkonkäyttöä lasketaan. Ei tavallisten sovellusten käyttöön."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"käytä ilmoituksia"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Antaa sovelluksen noutaa, tutkia ja tyhjentää ilmoituksia (myös muiden sovelluksien lähettämiä)."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"sido ilmoituskuuntelijapalveluun"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 549c4ae..6245112 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permet à l\'application de gérer les stratégies du réseau et de définir celles qui sont spécifiques à l\'application."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modifier le système de comptabilisation de l\'utilisation du réseau"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permet à l\'application de modifier l\'utilisation du réseau par les autres applications. Les applications standards ne doivent pas utiliser cette fonctionnalité."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"accéder aux notifications"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permet aux applications de récupérer, d\'examiner et d\'autoriser les notifications, y compris celles envoyées par d\'autres applications."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"s\'associer à l\'interface de niveau supérieur d\'un service d\'écoute des notifications"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 768dbd1..c55cc2f 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"एप्‍लिकेशन को नेटवर्क नीतियां प्रबंधित करने और एप्‍लिकेशन-विशिष्‍ट नियमों को परिभाषित करने देता है."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"नेटवर्क उपयोग हिसाब बदलें"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"एप्लिकेशन को यह संशोधित करने देता है कि एप्‍लिकेशन की तुलना में नेटवर्क उपयोग का मूल्यांकन कैसे किया जाता है. सामान्‍य एप्‍लिकेशन द्वारा उपयोग करने के लिए नहीं."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"सूचनाओं तक पहुंचें"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"एप्लिकेशन को सूचनाओं को प्राप्त करने, जांच करने, और साफ़ करने देता है, जिनमें अन्य एप्लिकेशन के द्वारा पोस्ट की गई सूचनाएं भी शामिल हैं."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"सूचना श्रवणकर्ता सेवा से जुड़ें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 730fb21..a73f79f 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Aplikaciji omogućuje upravljanje mrežnim pravilima i određivanje pravila za aplikacije."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"izmjena evidencije mrežne upotrebe"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Omogućuje aplikaciji izmjenu načina upotrebe mreže u odnosu na aplikacije. Nije namijenjeno uobičajenim aplikacijama."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"pristup obavijestima"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Omogućuje aplikaciji dohvaćanje, pregledavanje i brisanje obavijesti, uključujući obavijesti drugih aplikacija."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vezanje uz uslugu slušatelja obavijesti"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 23f8e74..42ea215 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Lehetővé teszi az alkalmazás számára, hogy kezelje a hálózati irányelveket és meghatározza az alkalmazásspecifikus szabályokat."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"hálózathasználat elszámolásának módosítása"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Lehetővé teszi az alkalmazás számára annak módosítását, hogy a hálózathasználatot hogyan számolják el az alkalmazások esetében. Normál alkalmazások nem használhatják."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"hozzáférési értesítések"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Lehetővé teszi, hogy az alkalmazás értesítéseket kérdezzen le, vizsgáljon és tisztítson meg, beleértve az egyéb alkalmazások által közzétett értesítéseket is."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"csatlakozzon értesítésfigyelő szolgáltatáshoz"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index ee652c5..9f8881b 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Mengizinkan apl mengelola kebijakan jaringan dan menentukan peraturan khusus apl."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"mengubah penghitungan penggunaan jaringan"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Mengizinkan apl memodifikasi cara penggunaan jaringan diperhitungkan terhadap apl. Tidak untuk digunakan oleh apl normal."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"mengakses pemberitahuan"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Mengizinkan aplikasi mengambil, memeriksa, dan menghapus pemberitahuan, termasuk pemberitahuan yang diposkan oleh aplikasi lain."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"mengikat layanan pendengar pemberitahuan"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4059ad5..83a0bb6 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Consente all\'applicazione di gestire le norme di rete e definire le regole specifiche delle applicazioni."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modifica calcolo dell\'utilizzo della rete"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Consente all\'applicazione di modificare il calcolo dell\'utilizzo della rete tra le applicazioni. Da non usare per normali applicazioni."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"accesso a notifiche"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Consente all\'app di recuperare, esaminare e cancellare notifiche, comprese quelle pubblicate da altre app."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincolo a un servizio listener di notifica"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 6168fdf..f864ba5 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"מאפשר ליישום לנהל מדיניות הרשת להגדיר כללים ספציפיים-ליישום."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"שנה ניהול חשבונות של שימוש ברשת"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"הרשאה זו מאפשרת ליישום לשנות את אופן החישוב של נתוני שימוש ברשת מול כל יישום. לא מיועד לשימוש ביישומים רגילים."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"גישה להתראות"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"מאפשר ליישום לאחזר, לבדוק ולמחוק התראות, כולל כאלה שפורסמו על ידי יישומים אחרים."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"איגוד לשירות של מאזין להתראות"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index eb1556d..3dba541 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"ネットワークポリシーを管理しアプリ固有のルールを定義することをアプリに許可します。"</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"ネットワークの課金の変更"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"アプリに対するネットワーク利用の計算方法を変更することをアプリに許可します。通常のアプリでは使用しません。"</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"通知にアクセス"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"通知(他のアプリから投稿されたものも含む)を取得、調査、クリアすることをアプリに許可します。"</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"通知リスナーサービスにバインド"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 8cd152d..8cafa31 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"앱이 네트워크 정책을 관리하고 앱별 규칙을 정의할 수 있도록 허용합니다."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"네트워크 사용량 계산 수정"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"애플리케이션이 애플리케이션의 네트워크 사용량을 계산하는 방식을 수정할 수 있도록 허용합니다. 일반 애플리케이션에서는 사용하지 않습니다."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"알림 액세스"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"앱이 다른 앱에서 게시한 알림을 비롯하여 알림을 검색하고 살펴보며 삭제할 수 있도록 허용합니다."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"알림 수신기 서비스 사용"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ea10f2f..6335bb7 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Leidžiama programai valdyti tinklo politiką ir apibrėžti konkrečios programos taisykles."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"keisti tinklo naudojimo apskaitą"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Leidžiama programai keisti, kaip tinklas naudojamas, palyginti su programomis. Neskirta naudoti įprastoms programoms."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"pasiekti pranešimus"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Programai leidžiama gauti, patikrinti ir išvalyti pranešimus, įskaitant pranešimus, kuriuos paskelbė kitos programos."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"susisaistyti su pranešimų skaitymo priemonės paslauga"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 3e3ab2c..6d14fed 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Ļauj lietotnei pārvaldīt tīkla politikas un noteikt lietotnes kārtulas."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"Tīkla lietojuma uzskaites mainīšana"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Ļauj lietotnei mainīt to, kā tīkla lietojums tiek uzskaitīts saistībā ar lietotnēm. Atļauja neattiecas uz parastām lietotnēm."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"piekļuve paziņojumiem"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Ļauj lietotnei izgūt, pārbaudīt un dzēst paziņojumus, tostarp lietotņu publicētos paziņojumus."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"saites izveidošana ar paziņojumu uztvērēja pakalpojumu"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 9260061..aa6a553 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Membenarkan apl mengurus dasar rangkaian dan menentukan peraturan khusus apl."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"ubah suai perakaunan penggunaan rangkaian"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Membenarkan apl untuk mengubah suai bagaimana penggunaan rangkaian diambil kira terhadap apl. Bukan untuk digunakan oleh apl biasa."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"pemberitahuan akses"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Membenarkan apl untuk mendapatkan semula, memeriksa dan memadam bersih pemberitahuan, termasuk yang disiarkan oleh apl lain."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"ikat kepada perkhidmatan pendengar pemberitahuan"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index b998377..fdcf826 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Lar appen administrere retningslinjene for nettverket og definere appspesifikke regler."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"Modifisering av regnskapsføring av nettverksbruk"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Lar appen endre hvordan nettverksbruk regnes ut for apper. Ikke beregnet på vanlige apper."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"varseltilgang"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Lar appen hente, gjennomgå og fjerne varsler, inkludert de som sendes fra andre apper."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"binding til en varsellyttertjeneste"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index af72700..327337c 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Hiermee kan de app het netwerkbeleid beheren en app-specifieke regels definiëren."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"verrekening van netwerkgebruik aanpassen"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Hiermee kan een app aanpassen hoe het netwerkgebruik wordt toegekend aan apps. Dit wordt niet gebruikt door normale apps."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"toegang tot meldingen"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Hiermee kan de app meldingen ophalen, onderzoeken en wissen, waaronder meldingen die zijn verzonden door andere apps."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"koppelen aan een listener-service voor meldingen"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 588df6f..c63baab 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Pozwala aplikacji na zarządzanie zasadami dotyczącymi sieci i definiowanie reguł aplikacji."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modyfikowanie sposobu naliczania użycia sieci"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Pozwala aplikacji na zmienianie sposobu rozliczania wykorzystania sieci przez aplikacje. Nieprzeznaczone dla zwykłych aplikacji."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"dostęp do powiadomień"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Umożliwia aplikacji pobieranie, sprawdzanie i usuwanie powiadomień, także tych, które pochodzą z innych aplikacji."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"utwórz połączenie z usługą odbiornika powiadomień"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 4d22c52..abb2a95 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permite que a aplicação faça a gestão de políticas de rede e defina regras específicas de aplicações."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modificar contabilização da utilização da rede"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que a aplicação modifique o modo como a utilização da rede é contabilizada em relação a aplicações. Nunca é necessário para aplicações normais."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"aceder às notificações"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que a aplicação obtenha, examine e limpe notificações, incluindo as que foram publicadas por outras aplicações."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"vincular a um serviço de escuta de notificações"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d9a997e..5cd4710 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permite que o aplicativo gerencie políticas de rede e definia regras específicas para o aplicativo."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modificar contagem de uso da rede"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite que o aplicativo modifique como o uso da rede é contabilizado em relação aos aplicativos. Não deve ser usado em aplicativos normais."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"acessar notificações"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite que o aplicativo recupere, examine e limpe notificações, inclusive as postadas por outros aplicativos."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"sujeitar a um serviço ouvinte de notificações"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index c5b4fd1..6e578c7 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1064,6 +1064,10 @@
     <skip />
     <!-- no translation found for permdesc_modifyNetworkAccounting (5443412866746198123) -->
     <skip />
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <!-- no translation found for permlab_accessNotifications (7673416487873432268) -->
     <skip />
     <!-- no translation found for permdesc_accessNotifications (458457742683431387) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index a058815..42c7ca3 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Permite aplicaţiei să gestioneze politicile de reţea şi să definească regulile specifice aplicaţiilor."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"modificaţi modul de calcul al utilizării reţelei"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Permite aplicaţiei să modifice modul în care este calculată utilizarea reţelei pentru aplicaţii. Nu se utilizează de aplicaţiile obişnuite."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"accesare notificări"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Permite aplicației să recupereze, să examineze și să șteargă notificări, inclusiv pe cele postate de alte aplicații."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"conectare la un serviciu de citire a notificărilor"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index b742263..b2dd07d 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Приложение сможет управлять сетевыми политиками и определять правила для отдельных приложений."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"Изменение учета использования сети"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Приложение сможет изменять порядок расчета использования сетевых ресурсов различными программами. Это разрешение не используется обычными приложениями."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"Доступ к уведомлениям"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Приложение сможет получать, проверять и удалять уведомления, включая те, что опубликованы другими приложениями."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"Подключение к службе просмотра уведомлений"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index f6a62cd..dd30fb4 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Umožňuje aplikácii spravovať pravidlá siete a definovať pravidlá pre konkrétnu aplikáciu."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"zmeniť kontrolu používania siete"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Umožňuje aplikácii upraviť používanie siete jednotlivými aplikáciami. Bežné aplikácie toto nastavenie nepoužívajú."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"prístup k upozorneniam"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Umožňuje aplikácii načítať, zobrazovať a mazať upozornenia vrátane tých, ktoré boli uverejnené inými aplikáciami."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"naviazanie sa na službu na počúvanie upozornení"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index ffbae22..46fa56a 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Programu omogoča upravljanje pravilnikov o omrežju in določanje pravil za program."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"spremeni obračunavanje uporabe omrežja"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Programu omogoča, da spremeni uporabo omrežja na podlagi programov. Ni za uporabo z navadnimi programi."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"dostop do obvestil"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Dovoli aplikaciji, da prenese, razišče in izbriše obvestila, tudi tista, ki so jih objavile druge aplikacije."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"poveži se s storitvijo poslušalca obvestil"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7465798..79626f8 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Дозвољава апликацији да управља смерницама за мрежу и одређује посебна правила за апликацију."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"измените обрачунавање коришћења мреже"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Дозвољава апликацији да измени начин на који апликације користе мрежу. Не користе је уобичајене апликације."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"приступ обавештењима"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Дозвољава апликацији да преузима, испитује и брише обавештења, укључујући она која постављају друге апликације."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"повезивање са услугом монитора обавештења"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 5e39d66..1618e2f 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Tillåter att appen hanterar nätverkspolicyer och definierar appspecifika regler."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"ändra nätverksredovisningen"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Tillåter att appen ändrar hur nätverksanvändning redovisas för appar. Används inte av vanliga appar."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"få åtkomst till meddelanden"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Tillåter att appen hämtar, granskar och raderar meddelanden, även sådana som skickats av andra appar."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"binda till en meddelandelyssnare"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 29ef9b3..14fbef8 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Inaruhusu programu kudhibiti sera za mtandao na kufafanua sheria maalum za programu."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"badilisha uthibitishaji wa matumizi ya mtandao"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Huruhusu programu kurekebisha jinsi matumizi ya mtandao yana hesabika dhidi ya programu. Sio ya matumizi na programu za kawaida."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"fikia arifa"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Huruhusu programu kurejesha, kuchunguza, na kuondoa arifa, ikiwa ni pamoja na zile zilizochapishwa na programu nyingine."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"unganisha kwenye huduma ya kisikilizi cha arifa"</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 9087783..7d804ca 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"อนุญาตให้แอปพลิเคชันจัดการนโยบายเครือข่ายและกำหนดกฎเฉพาะแอปพลิเคชัน"</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"แก้ไขการบันทึกบัญชีการใช้งานเครือข่าย"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"อนุญาตให้แอปพลิเคชันแก้ไขวิธีการบันทึกบัญชีการใช้งานเครือข่ายของแอปพลิเคชัน ไม่ใช้สำหรับแอปพลิเคชันทั่วไป"</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"เข้าถึงการแจ้งเตือน"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"ทำให้แอปสามารถเรียกดู ตรวจสอบ และล้างการแจ้งเตือนได้ ซึ่งรวมถึงการแจ้งเตือนที่โพสต์โดยแอปอื่นๆ ด้วย"</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"เชื่อมโยงกับบริการตัวฟังการแจ้งเตือน"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index d30fb77..50aea21 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Pinapayagan ang app na pamahalaan ang mga patakaran ng network at ilarawan ang mga patakarang tukoy sa app."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"baguhin ang pagkukuwenta sa paggamit ng network"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Pinapayagan ang app na baguhin kung paano isinasaalang-alang ang paggamit ng network laban sa apps. Hindi para sa paggamit ng normal na apps."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"i-access ang mga notification"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Pinapayagan ang app na kumuha, sumuri, at mag-clear ng mga notification, kabilang ang mga na-post ng iba pang apps."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"mapailalim sa isang serbisyo ng notification listener"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 30691d9..167b672 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Uygulamaya, ağ politikalarını yönetme ve uygulamaya özgü kuralları tanımlama izni verir."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"ağ kullanım hesaplamasını değiştir"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Uygulamaya, ağın uygulamalara göre nasıl kullanılacağını değiştirme izni verir. Normal uygulamalar tarafından kullanılmak için değildir."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"bildirimlere eriş"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Uygulamanın bildirimler almasına, bildirimleri incelemesine ve temizlemesine izin verir. Buna diğer uygulamalar tarafından yayınlanan bildirimler de dahildir."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bildirim dinleyici hizmetine bağlan"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index db27c85..a94925c 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Дозволяє програмі керувати політикою мережі та визначити спеціальні правила для програм."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"змінювати облік використання мережі"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Дозволяє програмі змінювати метод підрахунку того, як програми використовують мережу. Не для використання звичайними програмами."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"отримувати доступ до сповіщень"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Дозволяє програмі отримувати, перевіряти й очищати сповіщення, зокрема опубліковані іншими програмами."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"прив’язуватися до служби читання сповіщень"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 3002bcd..373fe3a 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Cho phép ứng dụng quản lý chính sách mạng và xác định quy tắc dành riêng cho ứng dụng."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"sửa đổi hạch toán sử dụng mạng"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Cho phép ứng dụng sửa đổi cách tính mức sử dụng mạng so với ứng dụng. Không dành cho các ứng dụng thông thường."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"truy cập thông báo"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Cho phép ứng dụng truy xuất, kiểm tra và xóa thông báo, bao gồm những thông báo được đăng bởi các ứng dụng khác."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"liên kết với dịch vụ trình xử lý thông báo"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 52526de..21fdaf16 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"允许应用管理网络政策和定义专门针对应用的规则。"</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"修改网络使用情况记录方式"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"允许该应用修改对于各应用的网络使用情况的统计方式。普通应用不应使用此权限。"</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"访问通知"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"允许该应用检索、检查并清除通知,包括其他应用发布的通知。"</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"绑定到通知侦听器服务"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 922a4f5..e627034 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"允許應用程式管理網路政策並定義應用程式專用規則。"</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"修改網路使用量計算方式"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"允許應用程式修改應用程式網路使用量的計算方式 (不建議一般應用程式使用)。"</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"存取通知"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"允許應用程式擷取、檢查及清除通知 (包括由其他應用程式發佈的通知)。"</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"繫結至通知接聽器服務"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 3eaf19f..af361ac 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -639,6 +639,10 @@
     <string name="permdesc_manageNetworkPolicy" msgid="7537586771559370668">"Ivumela insiza ukuthi yengamele iigomo iphinde ichaze imithetho ehambisana ngqo nensiza."</string>
     <string name="permlab_modifyNetworkAccounting" msgid="5088217309088729650">"lungisa ukubala kokusebenza kohleloxhumano"</string>
     <string name="permdesc_modifyNetworkAccounting" msgid="5443412866746198123">"Ivumela insiza ukuthi iguqule ukuthii ukusetshenziswa kwenethiwekhi kumiswa kanjani ezinsizeni. Ayisetshenziswa izinsiza ezijwayelekile."</string>
+    <!-- no translation found for permlab_markNetworkSocket (3658527214914959749) -->
+    <skip />
+    <!-- no translation found for permdesc_markNetworkSocket (7655568433696356578) -->
+    <skip />
     <string name="permlab_accessNotifications" msgid="7673416487873432268">"finyelela kuzaziso"</string>
     <string name="permdesc_accessNotifications" msgid="458457742683431387">"Ivumela uhlelo lokusebenza ukuthi lithole, lihlole, liphinde lisuse izaziso, ezifaka lezo ezithunyelwe ezinye izinhlelo zokusebenza."</string>
     <string name="permlab_bindNotificationListenerService" msgid="7057764742211656654">"bophezela kwisevisi yomlaleli wesaziso"</string>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 37c5d48..eba3f42 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4169,88 +4169,93 @@
 
     <!-- Printing -->
 
-    <!-- ISO A0 media size: 33.11" × 46.81" -->
+    <!-- ISO (European standard) A0 media (paper) size: 33.11" × 46.81" -->
     <string name="mediaSize_iso_a0">ISO A0</string>
-    <!-- ISO A1 media size: 23.39" × 33.11" -->
+    <!-- ISO (European standard) A1 media (paper) size: 23.39" × 33.11" -->
     <string name="mediaSize_iso_a1">ISO A1</string>
-    <!-- ISO A2 media size: 16.54" x 23.39" -->
+    <!-- ISO (European standard) A2 media (paper) size: 16.54" x 23.39" -->
     <string name="mediaSize_iso_a2">ISO A2</string>
-    <!-- ISO A3 media size: 11.69" x 16.54" -->
+    <!-- ISO (European standard) A3 media (paper) size: 11.69" x 16.54" -->
     <string name="mediaSize_iso_a3">ISO A3</string>
-    <!-- ISO A4 media size: 8.27" x 11.69" -->
+    <!-- ISO (European standard) A4 media (paper) size: 8.27" x 11.69" -->
     <string name="mediaSize_iso_a4">ISO A4</string>
-    <!-- ISO A5 media size: 5.83" x 8.27" -->
+    <!-- ISO (European standard) A5 media (paper) size: 5.83" x 8.27" -->
     <string name="mediaSize_iso_a5">ISO A5</string>
-    <!-- ISO A6 media size: 4.13" x 5.83" -->
+    <!-- ISO (European standard) A6 media (paper) size: 4.13" x 5.83" -->
     <string name="mediaSize_iso_a6">ISO A6</string>
-    <!-- ISO A7 media size: 2.91" x 4.13" -->
+    <!-- ISO (European standard) A7 media (paper) size: 2.91" x 4.13" -->
     <string name="mediaSize_iso_a7">ISO A7</string>
-    <!-- ISO A8 media size: 2.05" x 2.91" -->
+    <!-- ISO (European standard) A8 media (paper) size: 2.05" x 2.91" -->
     <string name="mediaSize_iso_a8">ISO A8</string>
-    <!-- ISO A9 media size: 1.46" x 2.05" -->
+    <!-- ISO (European standard) A9 media (paper) size: 1.46" x 2.05" -->
     <string name="mediaSize_iso_a9">ISO A9</string>
-    <!-- ISO A10 media size: 1.02" x 1.46" -->
+    <!-- ISO (European standard) A10 media (paper) size: 1.02" x 1.46" -->
     <string name="mediaSize_iso_a10">ISO A10</string>
 
-    <!-- ISO B0 media size: 39.37" x 55.67" -->
+    <!-- ISO (European standard) B0 media (paper) size: 39.37" x 55.67" -->
     <string name="mediaSize_iso_b0">ISO B0</string>
-    <!-- ISO B1 media size: 27.83" x 39.37" -->
+    <!-- ISO (European standard) B1 media (paper) size: 27.83" x 39.37" -->
     <string name="mediaSize_iso_b1">ISO B1</string>
-    <!-- ISO B2 media size - 19.69" x 27.83" -->
+    <!-- ISO (European standard) B2 media (paper) size - 19.69" x 27.83" -->
     <string name="mediaSize_iso_b2">ISO B2</string>
-    <!-- ISO B3 media size: 13.90" x 19.69" -->
+    <!-- ISO (European standard) B3 media (paper) size: 13.90" x 19.69" -->
     <string name="mediaSize_iso_b3">ISO B3</string>
-    <!-- ISO B4 media size: 9.84" x 13.90" -->
+    <!-- ISO (European standard) B4 media (paper) size: 9.84" x 13.90" -->
     <string name="mediaSize_iso_b4">ISO B4</string>
-    <!-- ISO B5 media size: 6.93" x 9.84" -->
+    <!-- ISO (European standard) B5 media (paper) size: 6.93" x 9.84" -->
     <string name="mediaSize_iso_b5">ISO B5</string>
-    <!-- ISO B6 media size: 4.92" x 6.93" -->
+    <!-- ISO (European standard) B6 media (paper) size: 4.92" x 6.93" -->
     <string name="mediaSize_iso_b6">ISO B6</string>
-    <!-- ISO B7 media size: 3.46" x 4.92" -->
+    <!-- ISO (European standard) B7 media (paper) size: 3.46" x 4.92" -->
     <string name="mediaSize_iso_b7">ISO B7</string>
-    <!-- ISO B8 media size: 2.44" x 3.46" -->
+    <!-- ISO (European standard) B8 media (paper) size: 2.44" x 3.46" -->
     <string name="mediaSize_iso_b8">ISO B8</string>
-    <!-- ISO B9 media size: 1.73" x 2.44" -->
+    <!-- ISO (European standard) B9 media (paper) size: 1.73" x 2.44" -->
     <string name="mediaSize_iso_b9">ISO B9</string>
-    <!-- ISO B10 media size: 1.22" x 1.73" -->
+    <!-- ISO (European standard) B10 media (paper) size: 1.22" x 1.73" -->
     <string name="mediaSize_iso_b10">ISO B10</string>
 
-    <!-- ISO C0 media size: 36.10" x 51.06" -->
+    <!-- ISO (European standard) C0 media (paper) size: 36.10" x 51.06" -->
     <string name="mediaSize_iso_c0">ISO C0</string>
-    <!-- ISO C1 media size: 25.51" x 36.10" -->
+    <!-- ISO (European standard) C1 media (paper) size: 25.51" x 36.10" -->
     <string name="mediaSize_iso_c1">ISO C1</string>
-    <!-- ISO C2 media size: 18.03" x 25.51" -->
+    <!-- ISO (European standard) C2 media (paper) size: 18.03" x 25.51" -->
     <string name="mediaSize_iso_c2">ISO C2</string>
-    <!-- ISO C3 media size: 12.76" x 18.03" -->
+    <!-- ISO (European standard) C3 media (paper) size: 12.76" x 18.03" -->
     <string name="mediaSize_iso_c3">ISO C3</string>
-    <!-- ISO C4 media size: 9.02" x 12.76" -->
+    <!-- ISO (European standard) C4 media (paper) size: 9.02" x 12.76" -->
     <string name="mediaSize_iso_c4">ISO C4</string>
-    <!-- ISO C5 media size: 6.38" x 9.02" -->
+    <!-- ISO (European standard) C5 media (paper) size: 6.38" x 9.02" -->
     <string name="mediaSize_iso_c5">ISO C5</string>
-    <!-- ISO C6 media size: 4.49" x 6.38" -->
+    <!-- ISO (European standard) C6 media (paper) size: 4.49" x 6.38" -->
     <string name="mediaSize_iso_c6">ISO C6</string>
-    <!-- ISO C7 media size: 3.19" x 4.49" -->
+    <!-- ISO (European standard) C7 media (paper) size: 3.19" x 4.49" -->
     <string name="mediaSize_iso_c7">ISO C7</string>
-    <!-- ISO ISO C8 media size: 2.24" x 3.19" -->
+    <!-- ISO ISO C8 media (paper) size: 2.24" x 3.19" -->
     <string name="mediaSize_iso_c8">ISO C8</string>
-    <!-- ISO ISO C9 media size: 1.57" x 2.24" -->
+    <!-- ISO ISO C9 media (paper) size: 1.57" x 2.24" -->
     <string name="mediaSize_iso_c9">ISO C9</string>
-    <!-- ISO C10 media size: 1.10" x 1.57" -->
+    <!-- ISO (European standard) C10 media (paper) size: 1.10" x 1.57" -->
     <string name="mediaSize_iso_c10">ISO C10</string>
 
-    <!-- North America Letter media size: 8.5" × 11" -->
+    <!-- North America Letter media (paper) size: 8.5" × 11" -->
     <string name="mediaSize_na_letter">Letter</string>
-    <!-- North America Government Letter media size: 8.0" × 10.5" -->
+    <!-- North America Government Letter media (paper) size: 8.0" × 10.5" -->
     <string name="mediaSize_na_gvrnmt_letter">Government Letter</string>
-    <!-- North America Legal media size: 8.5" × 14" -->
+    <!-- North America Legal media (paper) size: 8.5" × 14" -->
     <string name="mediaSize_na_legal">Legal</string>
-    <!-- North America Junior Legal media size: 8.0" × 5.0" -->
+    <!-- North America Junior Legal media (paper) size: 8.0" × 5.0" -->
     <string name="mediaSize_na_junior_legal">Junior Legal</string>
-    <!-- North America Ledger media size: 17" × 11" -->
+    <!-- North America Ledger media (paper) size: 17" × 11" -->
     <string name="mediaSize_na_ledger">Ledger</string>
-    <!-- North America Tabloid media size: 11" × 17" -->
+    <!-- North America Tabloid media (paper) size: 11" × 17" -->
     <string name="mediaSize_na_tabloid">Tabloid</string>
 
+    <!-- Write fail reason: printing was cancelled.[CHAR LIMIT=none] -->
+    <string name="write_fail_reason_cancelled">Cancelled</string>
+    <!-- Write fail reason: couldn't write the printed content. [CHAR LIMIT=none] -->
+    <string name="write_fail_reason_cannot_write">Error writing content</string>
+
     <!-- PIN creation dialog message [CHAR LIMIT=none] -->
     <string name="restr_pin_create_pin">Create a PIN for modifying restrictions</string>
     <!-- PIN entry dialog label for PIN [CHAR LIMIT=none] -->
@@ -4267,4 +4272,5 @@
         <item quantity="one">Incorrect PIN. Try again in 1 second.</item>
         <item quantity="other">Incorrect PIN. Try again in <xliff:g id="count">%d</xliff:g> seconds.</item>
     </plurals>
+
 </resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index b58fcfc..e29e82b 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -907,6 +907,8 @@
   <java-symbol type="string" name="mediaSize_na_ledger" />
   <java-symbol type="string" name="mediaSize_na_tabloid" />
   <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" />
 
   <java-symbol type="plurals" name="abbrev_in_num_days" />
   <java-symbol type="plurals" name="abbrev_in_num_hours" />
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 14ac901..fdec22b 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -513,12 +513,13 @@
     public native void skew(float sx, float sy);
 
     /**
-     * Preconcat the current matrix with the specified matrix.
+     * Preconcat the current matrix with the specified matrix. If the specified
+     * matrix is null, this method does nothing.
      *
      * @param matrix The matrix to preconcatenate with the current matrix
      */
     public void concat(Matrix matrix) {
-        native_concat(mNativeCanvas, matrix.native_instance);
+        if (matrix != null) native_concat(mNativeCanvas, matrix.native_instance);
     }
     
     /**
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 2b6f4cd..5ac2511 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -468,7 +468,11 @@
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("SetMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
+        if (mMatrix) {
+            OP_LOG("SetMatrix " MATRIX_STRING, MATRIX_ARGS(mMatrix));
+        } else {
+            OP_LOGS("SetMatrix (reset)");
+        }
     }
 
     virtual const char* name() { return "SetMatrix"; }
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 03f50c8..d233150 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -269,11 +269,14 @@
     }
 
     inline SkMatrix* refMatrix(SkMatrix* matrix) {
-        // Copying the matrix is cheap and prevents against the user changing the original
-        // matrix before the operation that uses it
-        SkMatrix* copy = new SkMatrix(*matrix);
-        mMatrices.add(copy);
-        return copy;
+        if (matrix) {
+            // Copying the matrix is cheap and prevents against the user changing
+            // the original matrix before the operation that uses it
+            SkMatrix* copy = new SkMatrix(*matrix);
+            mMatrices.add(copy);
+            return copy;
+        }
+        return matrix;
     }
 
     inline Layer* refLayer(Layer* layer) {
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 7866df4..5856057 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -541,7 +541,8 @@
 
     nativeFormat = Image_getPixelFormat(env, format);
 
-    sp<CpuConsumer> consumer = new CpuConsumer(maxImages);
+    sp<BufferQueue> bq = new BufferQueue();
+    sp<CpuConsumer> consumer = new CpuConsumer(bq, maxImages);
     // TODO: throw dvm exOutOfMemoryError?
     if (consumer == NULL) {
         jniThrowRuntimeException(env, "Failed to allocate native CpuConsumer");
diff --git a/media/mca/filterfw/native/core/gl_env.cpp b/media/mca/filterfw/native/core/gl_env.cpp
index 73768fe..63fd16e 100644
--- a/media/mca/filterfw/native/core/gl_env.cpp
+++ b/media/mca/filterfw/native/core/gl_env.cpp
@@ -160,7 +160,8 @@
   }
 
   // Create dummy surface using a GLConsumer
-  surfaceTexture_ = new GLConsumer(0);
+  sp<BufferQueue> bq = new BufferQueue();
+  surfaceTexture_ = new GLConsumer(bq, 0);
   window_ = new Surface(static_cast<sp<IGraphicBufferProducer> >(
           surfaceTexture_->getBufferQueue()));
 
diff --git a/packages/DocumentsUI/AndroidManifest.xml b/packages/DocumentsUI/AndroidManifest.xml
index ae11a8c..453ef45 100644
--- a/packages/DocumentsUI/AndroidManifest.xml
+++ b/packages/DocumentsUI/AndroidManifest.xml
@@ -22,7 +22,7 @@
         </activity>
 
         <!-- TODO: remove when we have real clients -->
-        <activity android:name=".TestActivity">
+        <activity android:name=".TestActivity" android:enabled="false">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.DEFAULT" />
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index ae2fe5c..659102c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -24,20 +24,21 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.IBinder.DeathRecipient;
 import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.UserHandle;
-import android.os.IBinder.DeathRecipient;
-import android.print.IPrintAdapter;
-import android.print.IPrintManager;
+import android.print.IPrintDocumentAdapter;
 import android.print.IPrinterDiscoveryObserver;
 import android.print.PageRange;
 import android.print.PrintAttributes;
 import android.print.PrintAttributes.MediaSize;
 import android.print.PrintAttributes.Resolution;
 import android.print.PrintAttributes.Tray;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
 import android.print.PrintJobInfo;
 import android.print.PrinterId;
 import android.print.PrinterInfo;
@@ -47,6 +48,7 @@
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.Log;
+import android.view.Choreographer;
 import android.view.Menu;
 import android.view.MenuItem;
 import android.view.View;
@@ -58,7 +60,6 @@
 import android.widget.Spinner;
 
 import java.io.File;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -78,9 +79,7 @@
 
     private static final int MIN_COPIES = 1;
 
-    private final List<QueuedAsyncTask<?>> mTaskQueue = new ArrayList<QueuedAsyncTask<?>>();
-
-    private IPrintManager mPrintManager;
+    private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
 
     private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
 
@@ -89,9 +88,7 @@
 
     private PrintAttributes mPrintAttributes;
 
-    private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
-
-    private RemotePrintAdapter mRemotePrintAdapter;
+    private RemotePrintDocumentAdapter mRemotePrintAdapter;
 
     // UI elements
 
@@ -124,11 +121,11 @@
     private Spinner mOrientationSpinner;
     public ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
 
-    private boolean mPrintStarted;
-
     private boolean mPrintConfirmed;
 
-    private IBinder mPrinable;
+    private boolean mStarted;
+
+    private IBinder mIPrintDocumentAdapter;
 
     // TODO: Implement store/restore state.
 
@@ -231,9 +228,6 @@
         getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN
                 | WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
 
-        mPrintManager = (IPrintManager) IPrintManager.Stub.asInterface(
-                ServiceManager.getService(PRINT_SERVICE));
-
         Bundle extras = getIntent().getExtras();
 
         mPrintJobId = extras.getInt(EXTRA_PRINT_JOB_ID, -1);
@@ -251,15 +245,16 @@
             mPrintAttributes = new PrintAttributes.Builder().create();
         }
 
-        mPrinable = extras.getBinder(EXTRA_PRINTABLE);
-        if (mPrinable == null) {
+        mIPrintDocumentAdapter = extras.getBinder(EXTRA_PRINTABLE);
+        if (mIPrintDocumentAdapter == null) {
             throw new IllegalArgumentException("Printable cannot be null");
         }
-        mRemotePrintAdapter = new RemotePrintAdapter(IPrintAdapter.Stub.asInterface(mPrinable),
+        mRemotePrintAdapter = new RemotePrintDocumentAdapter(
+                IPrintDocumentAdapter.Stub.asInterface(mIPrintDocumentAdapter),
                 mPrintSpooler.generateFileForPrintJob(mPrintJobId));
 
         try {
-            mPrinable.linkToDeath(mDeathRecipient, 0);
+            mIPrintDocumentAdapter.linkToDeath(mDeathRecipient, 0);
         } catch (RemoteException re) {
             finish();
         }
@@ -271,7 +266,7 @@
 
     @Override
     protected void onDestroy() {
-        mPrinable.unlinkToDeath(mDeathRecipient, 0);
+        mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
         super.onDestroy();
     }
 
@@ -361,13 +356,12 @@
         for (int i = 0; i < mediaSizeCount; i++) {
             MediaSize mediaSize = mediaSizes.get(i);
             mMediaSizeSpinnerAdapter.add(new SpinnerItem<MediaSize>(
-                    mediaSize, mediaSize.getLabel(getPackageManager())));
+                    mediaSize, mediaSize.getLabel()));
         }
         final int selectedMediaSizeIndex = mediaSizes.indexOf(
                 mPrintAttributes.getMediaSize());
         mMediaSizeSpinner.setOnItemSelectedListener(null);
         mMediaSizeSpinner.setSelection(selectedMediaSizeIndex);
-        mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
 
         // Resolution.
         mResolutionSpinnerAdapter.clear();
@@ -382,33 +376,49 @@
                 mPrintAttributes.getResolution());
         mResolutionSpinner.setOnItemSelectedListener(null);
         mResolutionSpinner.setSelection(selectedResolutionIndex);
-        mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+        // AdapterView has the weird behavior to notify the selection listener for a
+        // selection event that occurred *before* the listener was registered because
+        // it does the real selection change on the next layout pass. To avoid this
+        // behavior we re-attach the listener in the next traversal window - fun!
+        Choreographer.getInstance().postCallback(
+                Choreographer.CALLBACK_TRAVERSAL, new Runnable() {
+                    @Override
+                    public void run() {
+                        mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+                        mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+                    }
+                }, null);
 
         // Input tray.
         mInputTraySpinnerAdapter.clear();
         List<Tray> inputTrays = printer.getInputTrays();
-        final int inputTrayCount = inputTrays.size();
-        for (int i = 0; i < inputTrayCount; i++) {
-            Tray inputTray = inputTrays.get(i);
-            mInputTraySpinnerAdapter.add(new SpinnerItem<Tray>(
-                    inputTray, inputTray.getLabel(getPackageManager())));
+        if (inputTrays != null) {
+            final int inputTrayCount = inputTrays.size();
+            for (int i = 0; i < inputTrayCount; i++) {
+                Tray inputTray = inputTrays.get(i);
+                mInputTraySpinnerAdapter.add(new SpinnerItem<Tray>(
+                        inputTray, inputTray.getLabel(getPackageManager())));
+            }
+            final int selectedInputTrayIndex = inputTrays.indexOf(
+                    mPrintAttributes.getInputTray());
+            mInputTraySpinner.setSelection(selectedInputTrayIndex);
         }
-        final int selectedInputTrayIndex = inputTrays.indexOf(
-                mPrintAttributes.getInputTray());
-        mInputTraySpinner.setSelection(selectedInputTrayIndex);
 
         // Output tray.
         mOutputTraySpinnerAdapter.clear();
         List<Tray> outputTrays = printer.getOutputTrays();
-        final int outputTrayCount = outputTrays.size();
-        for (int i = 0; i < outputTrayCount; i++) {
-            Tray outputTray = outputTrays.get(i);
-            mOutputTraySpinnerAdapter.add(new SpinnerItem<Tray>(
-                    outputTray, outputTray.getLabel(getPackageManager())));
+        if (outputTrays != null) {
+            final int outputTrayCount = outputTrays.size();
+            for (int i = 0; i < outputTrayCount; i++) {
+                Tray outputTray = outputTrays.get(i);
+                mOutputTraySpinnerAdapter.add(new SpinnerItem<Tray>(
+                        outputTray, outputTray.getLabel(getPackageManager())));
+            }
+            final int selectedOutputTrayIndex = outputTrays.indexOf(
+                    mPrintAttributes.getOutputTray());
+            mOutputTraySpinner.setSelection(selectedOutputTrayIndex);
         }
-        final int selectedOutputTrayIndex = outputTrays.indexOf(
-                mPrintAttributes.getOutputTray());
-        mOutputTraySpinner.setSelection(selectedOutputTrayIndex);
 
         // Duplex mode.
         final int duplexModes = printer.getDuplexModes();
@@ -482,22 +492,14 @@
     @Override
     protected void onResume() {
         super.onResume();
-        try {
-            mPrintManager.startDiscoverPrinters(mPrinterDiscoveryObserver);
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error starting printer discovery!", re);
-        }
+        mPrintSpooler.startPrinterDiscovery(mPrinterDiscoveryObserver);
         notifyPrintableStartIfNeeded();
     }
 
     @Override
     protected void onPause() {
         super.onPause();
-        try {
-            mPrintManager.stopDiscoverPrinters();
-        } catch (RemoteException re) {
-            Log.e(LOG_TAG, "Error starting printer discovery!", re);
-        }
+        mPrintSpooler.stopPrinterDiscovery();
         notifyPrintableFinishIfNeeded();
     }
 
@@ -518,119 +520,83 @@
 
     private void notifyPrintableStartIfNeeded() {
         if (mDestinationSpinner.getSelectedItemPosition() < 0
-                || mPrintStarted) {
+                || mStarted) {
             return;
         }
-        mPrintStarted = true;
-        new QueuedAsyncTask<Void>(mTaskQueue) {
-            @Override
-            protected Void doInBackground(Void... params) {
-                try {
-                    mRemotePrintAdapter.start();
-                } catch (IOException ioe) {
-                    Log.e(LOG_TAG, "Error reading printed data!", ioe);
-                }
-                return null;
-            }
-
-            @Override
-            protected void onPostExecute(Void result) {
-                super.onPostExecute(result);
-                updatePrintableContentIfNeeded();
-            }
-        }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+        mStarted = true;
+        mRemotePrintAdapter.start();
+        updatePrintableContentIfNeeded();
     }
 
     private void updatePrintableContentIfNeeded() {
-        if (!mPrintStarted) {
+        if (!mStarted) {
             return;
         }
 
+        // TODO: Implement old attributes tracking
         mPrintSpooler.setPrintJobAttributes(mPrintJobId, mPrintAttributes);
 
-        // TODO: Implement page selector.
-        final List<PageRange> pages = new ArrayList<PageRange>();
-        pages.add(PageRange.ALL_PAGES);
-
-        new QueuedAsyncTask<File>(mTaskQueue) {
+        mRemotePrintAdapter.layout(new PrintAttributes.Builder().create(),
+                mPrintAttributes, new LayoutResultCallback() {
             @Override
-            protected File doInBackground(Void... params) {
-                try {
-                    mRemotePrintAdapter.printAttributesChanged(mPrintAttributes);
-                    mRemotePrintAdapter.cancelPrint();
-                    mRemotePrintAdapter.print(pages);
-                    return mRemotePrintAdapter.getFile();
-                } catch (IOException ioe) {
-                    Log.e(LOG_TAG, "Error reading printed data!", ioe);
-                }
-                return null;
+            public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+                // TODO: Handle the case of unchanged content
+                mPrintSpooler.setPrintJobPrintDocumentInfo(mPrintJobId, info);
+
+                // TODO: Implement page selector.
+                final List<PageRange> pages = new ArrayList<PageRange>();
+                pages.add(PageRange.ALL_PAGES);
+
+                mRemotePrintAdapter.write(pages, new WriteResultCallback() {
+                    @Override
+                    public void onWriteFinished(List<PageRange> pages) {
+                        updatePrintPreview(mRemotePrintAdapter.getFile());
+                    }
+
+                    @Override
+                    public void onWriteFailed(CharSequence error) {
+                        Log.e(LOG_TAG, "Error write layout: " + error);
+                        finishActivity(Activity.RESULT_CANCELED);
+                    }
+                });
             }
 
             @Override
-            protected void onPostExecute(File file) {
-                super.onPostExecute(file);
-                updatePrintPreview(file);
+            public void onLayoutFailed(CharSequence error) {
+                Log.e(LOG_TAG, "Error during layout: " + error);
+                finishActivity(Activity.RESULT_CANCELED);
             }
-        }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+        });
     }
 
     private void notifyPrintableFinishIfNeeded() {
-        if (!mPrintStarted) {
+        if (!mStarted) {
             return;
         }
-        mPrintStarted = false;
 
-        // Cancel all pending async tasks if the activity was canceled.
         if (!mPrintConfirmed) {
-            final int taskCount = mTaskQueue.size();
-            for (int i = taskCount - 1; i >= 0; i--) {
-                mTaskQueue.remove(i).cancel();
-            }
+            mRemotePrintAdapter.cancel();
+        }
+        mRemotePrintAdapter.finish();
+
+        // If canceled or no printer, nothing to do.
+        final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
+        if (!mPrintConfirmed || selectedIndex < 0) {
+            // Update the print job's status.
+            mPrintSpooler.setPrintJobState(mPrintJobId,
+                    PrintJobInfo.STATE_CANCELED);
+            return;
         }
 
-        new AsyncTask<Void, Void, Void>() {
-            @Override
-            protected Void doInBackground(Void... params) {
-                // Notify the app that printing completed.
-                try {
-                    mRemotePrintAdapter.finish();
-                } catch (IOException ioe) {
-                    Log.e(LOG_TAG, "Error reading printed data!", ioe);
-                }
+        // Update the print job's printer.
+        SpinnerItem<PrinterInfo> printerItem =
+                mDestinationSpinnerAdapter.getItem(selectedIndex);
+        PrinterId printerId =  printerItem.value.getId();
+        mPrintSpooler.setPrintJobPrinterId(mPrintJobId, printerId);
 
-                // If canceled, nothing to do.
-                if (!mPrintConfirmed) {
-                    mPrintSpooler.setPrintJobState(mPrintJobId,
-                            PrintJobInfo.STATE_CANCELED);
-                    return null;
-                }
-
-                // No printer, nothing to do.
-                final int selectedIndex = mDestinationSpinner.getSelectedItemPosition();
-                if (selectedIndex < 0) {
-                    // Update the print job's status.
-                    mPrintSpooler.setPrintJobState(mPrintJobId,
-                            PrintJobInfo.STATE_CANCELED);
-                    return null;
-                }
-
-                // Update the print job's printer.
-                SpinnerItem<PrinterInfo> printerItem =
-                        mDestinationSpinnerAdapter.getItem(selectedIndex);
-                PrinterId printerId =  printerItem.value.getId();
-                mPrintSpooler.setPrintJobPrinterId(mPrintJobId, printerId);
-
-                // Update the print job's status.
-                mPrintSpooler.setPrintJobState(mPrintJobId,
-                        PrintJobInfo.STATE_QUEUED);
-                return null;
-            }
-
-            // Important: If we are canceling, then we do not wait for the write
-            // to complete since the result will be discarded anyway, we simply
-            // execute the finish immediately which will interrupt the write.
-        }.executeOnExecutor(mPrintConfirmed ? AsyncTask.SERIAL_EXECUTOR
-                : AsyncTask.THREAD_POOL_EXECUTOR, (Void[]) null);
+        // Update the print job's status.
+        mPrintSpooler.setPrintJobState(mPrintJobId,
+                PrintJobInfo.STATE_QUEUED);
 
         if (DEBUG) {
             if (mPrintConfirmed) {
@@ -689,30 +655,7 @@
         }
     }
 
-    private abstract class QueuedAsyncTask<T> extends AsyncTask<Void, Void, T> {
-
-        private final List<QueuedAsyncTask<?>> mPendingOrRunningTasks;
-
-        public QueuedAsyncTask(List<QueuedAsyncTask<?>> pendingOrRunningTasks) {
-            mPendingOrRunningTasks = pendingOrRunningTasks;
-        }
-
-        @Override
-        protected void onPreExecute() {
-            mPendingOrRunningTasks.add(this);
-        }
-
-        @Override
-        protected void onPostExecute(T result) {
-            mPendingOrRunningTasks.remove(this);
-        }
-
-        public void cancel() {
-            super.cancel(true);
-            mPendingOrRunningTasks.remove(this);
-        }
-    }
-
+    // Caution: Use this only for debugging
     private final class ViewSpooledFileAsyncTask extends AsyncTask<Void, Void, Void> {
 
         private final File mFile;
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
index 2b27b69..0546a43 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
@@ -16,30 +16,18 @@
 
 package com.android.printspooler;
 
-import java.io.Closeable;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 import android.content.ComponentName;
 import android.content.Context;
 import android.os.AsyncTask;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.print.IPrintClient;
-import android.print.IPrintManager;
+import android.print.IPrintSpoolerClient;
+import android.print.IPrinterDiscoveryObserver;
 import android.print.PrintAttributes;
 import android.print.PrintJobInfo;
 import android.print.PrintManager;
+import android.print.PrintDocumentInfo;
 import android.print.PrinterId;
 import android.util.AtomicFile;
 import android.util.Log;
@@ -48,6 +36,22 @@
 
 import com.android.internal.util.FastXmlSerializer;
 
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
 public class PrintSpooler {
 
     private static final String LOG_TAG = PrintSpooler.class.getSimpleName();
@@ -64,17 +68,17 @@
 
     private static final Object sLock = new Object();
 
-    private final Object mLock = new Object();
-
     private static PrintSpooler sInstance;
 
+    private final Object mLock = new Object();
+
     private final List<PrintJobInfo> mPrintJobs = new ArrayList<PrintJobInfo>();
 
     private final PersistenceManager mPersistanceManager;
 
     private final Context mContext;
 
-    private final IPrintManager mPrintManager;
+    public IPrintSpoolerClient mClient;
 
     public static PrintSpooler getInstance(Context context) {
         synchronized (sLock) {
@@ -89,8 +93,40 @@
         mContext = context;
         mPersistanceManager = new PersistenceManager();
         mPersistanceManager.readStateLocked();
-        mPrintManager = IPrintManager.Stub.asInterface(
-                ServiceManager.getService("print"));
+    }
+
+    public void setCleint(IPrintSpoolerClient client) {
+        synchronized (mLock) {
+            mClient = client;
+        }
+    }
+
+    public void startPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+        IPrintSpoolerClient client = null;
+        synchronized (mLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                client.onStartPrinterDiscovery(observer);
+            } catch (RemoteException re) {
+                Log.e(LOG_TAG, "Error notifying start printer discovery.", re);
+            }
+        }
+    }
+
+    public void stopPrinterDiscovery() {
+        IPrintSpoolerClient client = null;
+        synchronized (mLock) {
+            client = mClient;
+        }
+        if (client != null) {
+            try {
+                client.onStopPrinterDiscovery();
+            } catch (RemoteException re) {
+                Log.e(LOG_TAG, "Error notifying stop printer discovery.", re);
+            }
+        }
     }
 
     public List<PrintJobInfo> getPrintJobs(ComponentName componentName, int state, int appId) {
@@ -102,7 +138,7 @@
                 PrinterId printerId = printJob.getPrinterId();
                 final boolean sameComponent = (componentName == null
                         || (printerId != null
-                        && componentName.equals(printerId.getServiceComponentName())));
+                        && componentName.equals(printerId.getService())));
                 final boolean sameAppId = appId == PrintManager.APP_ID_ANY
                         || printJob.getAppId() == appId;
                 final boolean sameState = state == PrintJobInfo.STATE_ANY
@@ -137,15 +173,10 @@
             PrintJobInfo printJob = getPrintJob(printJobId, appId);
             if (printJob != null) {
                 switch (printJob.getState()) {
-                    case PrintJobInfo.STATE_CREATED: {
-                        removePrintJobLocked(printJob);
-                    } return true;
+                    case PrintJobInfo.STATE_CREATED:
                     case PrintJobInfo.STATE_QUEUED: {
-                        removePrintJobLocked(printJob);
+                        setPrintJobState(printJobId, PrintJobInfo.STATE_CANCELED);
                     } return true;
-                    default: {
-                        return false;
-                    }
                 }
             }
             return false;
@@ -169,6 +200,72 @@
         }
     }
 
+    public void notifyClientForActivteJobs() {
+        IPrintSpoolerClient client = null;
+        Map<ComponentName, List<PrintJobInfo>> activeJobsPerServiceMap =
+                new HashMap<ComponentName, List<PrintJobInfo>>();
+
+        synchronized(mLock) {
+            if (mClient == null) {
+                throw new IllegalStateException("Client cannot be null.");
+            }
+            client = mClient;
+
+            final int printJobCount = mPrintJobs.size();
+            for (int i = 0; i < printJobCount; i++) {
+                PrintJobInfo printJob = mPrintJobs.get(i);
+                switch (printJob.getState()) {
+                    case PrintJobInfo.STATE_CREATED: {
+                        /* skip - not ready to be handled by a service */
+                    } break;
+
+                    case PrintJobInfo.STATE_QUEUED:
+                    case PrintJobInfo.STATE_STARTED: {
+                        ComponentName service = printJob.getPrinterId().getService();
+                        List<PrintJobInfo> jobsPerService = activeJobsPerServiceMap.get(service);
+                        if (jobsPerService == null) {
+                            jobsPerService = new ArrayList<PrintJobInfo>();
+                            activeJobsPerServiceMap.put(service, jobsPerService);
+                        }
+                        jobsPerService.add(printJob);
+                    } break;
+
+                    default: {
+                        ComponentName service = printJob.getPrinterId().getService();
+                        if (!activeJobsPerServiceMap.containsKey(service)) {
+                            activeJobsPerServiceMap.put(service, null);
+                        }
+                    }
+                }
+            }
+        }
+
+        boolean allPrintJobsHandled = true;
+
+        for (Map.Entry<ComponentName, List<PrintJobInfo>> entry
+                : activeJobsPerServiceMap.entrySet()) {
+            ComponentName service = entry.getKey();
+            List<PrintJobInfo> printJobs = entry.getValue();
+
+            if (printJobs != null) {
+                allPrintJobsHandled = false;
+                final int printJobCount = printJobs.size();
+                for (int i = 0; i < printJobCount; i++) {
+                    PrintJobInfo printJob = printJobs.get(i);
+                    if (printJob.getState() == PrintJobInfo.STATE_QUEUED) {
+                        callOnPrintJobQueuedQuietly(client, printJob);
+                    }
+                }
+            } else {
+                callOnAllPrintJobsForServiceHandledQuietly(client, service);
+            }
+        }
+
+        if (allPrintJobsHandled) {
+            callOnAllPrintJobsHandledQuietly(client);
+        }
+    }
+
     private int generatePrintJobIdLocked() {
         int printJobId = sPrintJobIdCounter++;
         while (isDuplicatePrintJobId(printJobId)) {
@@ -213,24 +310,14 @@
             } catch (IOException ioe) {
                 Log.e(LOG_TAG, "Error writing print job data!", ioe);
             } finally {
-                closeIfNotNullNoException(in);
-                closeIfNotNullNoException(out);
-                closeIfNotNullNoException(fd);
+                IoUtils.closeQuietly(in);
+                IoUtils.closeQuietly(out);
+                IoUtils.closeQuietly(fd);
             }
         }
         return false;
     }
 
-    private void closeIfNotNullNoException(Closeable closeable) {
-        if (closeable != null) {
-            try {
-                closeable.close();
-            } catch (IOException ioe) {
-                /* ignore */;
-            }
-        }
-    }
-
     public File generateFileForPrintJob(int printJobId) {
         return new File(mContext.getFilesDir(), "print_job_"
                 + printJobId + "." + PRINT_FILE_EXTENSION);
@@ -254,9 +341,20 @@
 
     public boolean setPrintJobState(int printJobId, int state) {
         boolean success = false;
+
+        boolean allPrintJobsHandled = false;
+        boolean allPrintJobsForServiceHandled = false;
+
+        IPrintSpoolerClient client = null;
         PrintJobInfo queuedPrintJob = null;
+        PrintJobInfo removedPrintJob = null;
 
         synchronized (mLock) {
+            if (mClient == null) {
+                throw new IllegalStateException("Client cannot be null.");
+            }
+            client = mClient;
+
             PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
             if (printJob != null && printJob.getState() < state) {
                 success = true;
@@ -265,8 +363,23 @@
                 switch (state) {
                     case PrintJobInfo.STATE_COMPLETED:
                     case PrintJobInfo.STATE_CANCELED: {
+                        removedPrintJob = printJob;
                         removePrintJobLocked(printJob);
+
+                        // No printer means creation of a print job was cancelled,
+                        // therefore the state of the spooler did not change and no
+                        // notifications are needed. We also do not need to persist
+                        // the state.
+                        PrinterId printerId = printJob.getPrinterId();
+                        if (printerId == null) {
+                            return true;
+                        }
+
+                        allPrintJobsHandled = !hasActivePrintJobsLocked();
+                        allPrintJobsForServiceHandled = !hasActivePrintJobsForServiceLocked(
+                                printerId.getService());
                     } break;
+
                     case PrintJobInfo.STATE_QUEUED: {
                         queuedPrintJob = new PrintJobInfo(printJob);
                     } break;
@@ -279,17 +392,77 @@
         }
 
         if (queuedPrintJob != null) {
-            try {
-                mPrintManager.onPrintJobQueued(queuedPrintJob.getPrinterId(),
-                        queuedPrintJob);
-            } catch (RemoteException re) {
-                /* ignore */
-            }
+            callOnPrintJobQueuedQuietly(client, queuedPrintJob);
+        }
+
+        if (allPrintJobsForServiceHandled) {
+            callOnAllPrintJobsForServiceHandledQuietly(client,
+                        removedPrintJob.getPrinterId().getService());
+        }
+
+        if (allPrintJobsHandled) {
+            callOnAllPrintJobsHandledQuietly(client);
         }
 
         return success;
     }
 
+    private void callOnPrintJobQueuedQuietly(IPrintSpoolerClient client,
+            PrintJobInfo printJob) {
+        try {
+            client.onPrintJobQueued(printJob);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error notify for a queued print job.", re);
+        }
+    }
+
+    private void callOnAllPrintJobsForServiceHandledQuietly(IPrintSpoolerClient client,
+            ComponentName service) {
+        try {
+            client.onAllPrintJobsForServiceHandled(service);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error notify for all print jobs per service handled.", re);
+        }
+    }
+
+    private void callOnAllPrintJobsHandledQuietly(IPrintSpoolerClient client) {
+        try {
+            client.onAllPrintJobsHandled();
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
+        }
+    }
+
+    private boolean hasActivePrintJobsLocked() {
+        final int printJobCount = mPrintJobs.size();
+        for (int i = 0; i < printJobCount; i++) {
+            PrintJobInfo printJob = mPrintJobs.get(i);
+            switch (printJob.getState()) {
+                case PrintJobInfo.STATE_QUEUED:
+                case PrintJobInfo.STATE_STARTED: {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    private boolean hasActivePrintJobsForServiceLocked(ComponentName service) {
+        final int printJobCount = mPrintJobs.size();
+        for (int i = 0; i < printJobCount; i++) {
+            PrintJobInfo printJob = mPrintJobs.get(i);
+            switch (printJob.getState()) {
+                case PrintJobInfo.STATE_QUEUED:
+                case PrintJobInfo.STATE_STARTED: {
+                    if (printJob.getPrinterId().getService().equals(service)) {
+                        return true;
+                    }
+                } break;
+            }
+        }
+        return false;
+    }
+
     public boolean setPrintJobTag(int printJobId, String tag) {
         synchronized (mLock) {
             PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
@@ -302,6 +475,18 @@
         return false;
     }
 
+    public final boolean setPrintJobPrintDocumentInfo(int printJobId, PrintDocumentInfo info) {
+        synchronized (mLock) {
+            PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+            if (printJob != null) {
+                printJob.setDocumentInfo(info);
+                mPersistanceManager.writeStateLocked();
+                return true;
+            }
+        }
+        return false;
+    }
+
     public void setPrintJobAttributes(int printJobId, PrintAttributes attributes) {
         synchronized (mLock) {
             PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index 57c4557..050332c 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
@@ -29,10 +29,11 @@
 import android.os.Message;
 import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
-import android.print.IPrintAdapter;
+import android.print.IPrintDocumentAdapter;
 import android.print.IPrintClient;
-import android.print.IPrintSpoolerService;
-import android.print.IPrintSpoolerServiceCallbacks;
+import android.print.IPrintSpoolerClient;
+import android.print.IPrintSpooler;
+import android.print.IPrintSpoolerCallbacks;
 import android.print.PrintAttributes;
 import android.print.PrintJobInfo;
 import android.util.Slog;
@@ -64,21 +65,21 @@
 
     @Override
     public IBinder onBind(Intent intent) {
-        return new IPrintSpoolerService.Stub() {
+        return new IPrintSpooler.Stub() {
             @Override
-            public void getPrintJobs(IPrintSpoolerServiceCallbacks callback,
+            public void getPrintJobInfos(IPrintSpoolerCallbacks callback,
                     ComponentName componentName, int state, int appId, int sequence)
                             throws RemoteException {
                 List<PrintJobInfo> printJobs = null;
                 try {
                     printJobs = mSpooler.getPrintJobs(componentName, state, appId);
                 } finally {
-                    callback.onGetPrintJobsResult(printJobs, sequence);
+                    callback.onGetPrintJobInfosResult(printJobs, sequence);
                 }
             }
 
             @Override
-            public void getPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+            public void getPrintJobInfo(int printJobId, IPrintSpoolerCallbacks callback,
                     int appId, int sequence) throws RemoteException {
                 PrintJobInfo printJob = null;
                 try {
@@ -89,7 +90,7 @@
             }
 
             @Override
-            public void cancelPrintJob(int printJobId, IPrintSpoolerServiceCallbacks callback,
+            public void cancelPrintJob(int printJobId, IPrintSpoolerCallbacks callback,
                     int appId, int sequence) throws RemoteException {
                 boolean success = false;
                 try {
@@ -102,8 +103,8 @@
             @SuppressWarnings("deprecation")
             @Override
             public void createPrintJob(String printJobName, IPrintClient client,
-                    IPrintAdapter printAdapter, PrintAttributes attributes,
-                    IPrintSpoolerServiceCallbacks callback, int appId, int sequence)
+                    IPrintDocumentAdapter printAdapter, PrintAttributes attributes,
+                    IPrintSpoolerCallbacks callback, int appId, int sequence)
                             throws RemoteException {
                 PrintJobInfo printJob = null;
                 try {
@@ -134,7 +135,7 @@
 
             @Override
             public void setPrintJobState(int printJobId, int state,
-                    IPrintSpoolerServiceCallbacks callback, int sequece)
+                    IPrintSpoolerCallbacks callback, int sequece)
                             throws RemoteException {
                 boolean success = false;
                 try {
@@ -148,7 +149,7 @@
 
             @Override
             public void setPrintJobTag(int printJobId, String tag,
-                    IPrintSpoolerServiceCallbacks callback, int sequece)
+                    IPrintSpoolerCallbacks callback, int sequece)
                             throws RemoteException {
                 boolean success = false;
                 try {
@@ -162,6 +163,16 @@
             public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
                 mSpooler.writePrintJobData(fd, printJobId);
             }
+
+            @Override
+            public void setClient(IPrintSpoolerClient client)  {
+                mSpooler.setCleint(client);
+            }
+
+            @Override
+            public void notifyClientForActivteJobs() {
+                mSpooler.notifyClientForActivteJobs();
+            }
         };
     }
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java
deleted file mode 100644
index c81b00c..0000000
--- a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintAdapter.java
+++ /dev/null
@@ -1,219 +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.os.ICancellationSignal;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.print.IPrintAdapter;
-import android.print.IPrintResultCallback;
-import android.print.PageRange;
-import android.print.PrintAdapterInfo;
-import android.print.PrintAttributes;
-import android.util.Log;
-
-import libcore.io.IoUtils;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.util.List;
-
-/**
- * This class represents a remote print adapter instance.
- */
-final class RemotePrintAdapter {
-    private static final String LOG_TAG = "RemotePrintAdapter";
-
-    private static final boolean DEBUG = true;
-
-    private final Object mLock = new Object();
-
-    private final IPrintAdapter mRemoteInterface;
-
-    private final File mFile;
-
-    private final IPrintResultCallback mIPrintProgressListener;
-
-    private PrintAdapterInfo mInfo;
-
-    private ICancellationSignal mCancellationSignal;
-
-    private Thread mWriteThread;
-
-    public RemotePrintAdapter(IPrintAdapter printAdatper, File file) {
-        mRemoteInterface = printAdatper;
-        mFile = file;
-        mIPrintProgressListener = new IPrintResultCallback.Stub() {
-            @Override
-            public void onPrintStarted(PrintAdapterInfo info,
-                    ICancellationSignal cancellationSignal) {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "IPrintProgressListener#onPrintStarted()");
-                }
-                synchronized (mLock) {
-                    mInfo = info;
-                    mCancellationSignal = cancellationSignal;
-                }
-            }
-
-            @Override
-            public void onPrintFinished(List<PageRange> pages) {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "IPrintProgressListener#onPrintFinished(" + pages + ")");
-                }
-                synchronized (mLock) {
-                    if (isPrintingLocked()) {
-                        mWriteThread.interrupt();
-                        mCancellationSignal = null;
-                    }
-                }
-            }
-
-            @Override
-            public void onPrintFailed(CharSequence error) {
-                if (DEBUG) {
-                    Log.i(LOG_TAG, "IPrintProgressListener#onPrintFailed(" + error + ")");
-                }
-                synchronized (mLock) {
-                    if (isPrintingLocked()) {
-                        mWriteThread.interrupt();
-                        mCancellationSignal = null;
-                    }
-                }
-            }
-        };
-    }
-
-    public File getFile() {
-        if (DEBUG) {
-            Log.i(LOG_TAG, "getFile()");
-        }
-        return mFile;
-    }
-
-    public void start() throws IOException {
-        if (DEBUG) {
-            Log.i(LOG_TAG, "start()");
-        }
-        try {
-            mRemoteInterface.start();
-        } catch (RemoteException re) {
-            throw new IOException("Error reading file", re);
-        }
-    }
-
-    public void printAttributesChanged(PrintAttributes attributes) throws IOException {
-        if (DEBUG) {
-            Log.i(LOG_TAG, "printAttributesChanged(" + attributes +")");
-        }
-        try {
-            mRemoteInterface.printAttributesChanged(attributes);
-        } catch (RemoteException re) {
-            throw new IOException("Error reading file", re);
-        }
-    }
-
-    public void print(List<PageRange> pages) throws IOException {
-        if (DEBUG) {
-            Log.i(LOG_TAG, "print(" + pages +")");
-        }
-        InputStream in = null;
-        OutputStream out = null;
-        ParcelFileDescriptor source = null;
-        ParcelFileDescriptor sink = null;
-        synchronized (mLock) {
-            mWriteThread = Thread.currentThread();
-        }
-        try {
-            ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
-            source = pipe[0];
-            sink = pipe[1];
-
-            in = new FileInputStream(source.getFileDescriptor());
-            out = new FileOutputStream(mFile);
-
-            // Async call to initiate the other process writing the data.
-            mRemoteInterface.print(pages, sink, mIPrintProgressListener);
-
-            // Close the source. It is now held by the client.
-            sink.close();
-            sink = null;
-
-            final byte[] buffer = new byte[8192];
-            while (true) {
-                if (Thread.currentThread().isInterrupted()) {
-                    Thread.currentThread().interrupt();
-                    break;
-                }
-                final int readByteCount = in.read(buffer);
-                if (readByteCount < 0) {
-                    break;
-                }
-                out.write(buffer, 0, readByteCount);
-            }
-        } catch (RemoteException re) {
-            throw new IOException("Error reading file", re);
-        } catch (IOException ioe) {
-            throw new IOException("Error reading file", ioe);
-        } finally {
-            IoUtils.closeQuietly(in);
-            IoUtils.closeQuietly(out);
-            IoUtils.closeQuietly(sink);
-            IoUtils.closeQuietly(source);
-        }
-    }
-
-    public void cancelPrint() throws IOException {
-        if (DEBUG) {
-            Log.i(LOG_TAG, "cancelPrint()");
-        }
-        synchronized (mLock) {
-            if (isPrintingLocked()) {
-                try {
-                    mCancellationSignal.cancel();
-                } catch (RemoteException re) {
-                    throw new IOException("Error cancelling print", re);
-                }
-            }
-        }
-    }
-
-    public void finish() throws IOException {
-        if (DEBUG) {
-            Log.i(LOG_TAG, "finish()");
-        }
-        try {
-            mRemoteInterface.finish();
-        } catch (RemoteException re) {
-            throw new IOException("Error reading file", re);
-        }
-    }
-
-    public PrintAdapterInfo getInfo() {
-        synchronized (mLock) {
-            return mInfo;
-        }
-    }
-
-    private boolean isPrintingLocked() {
-        return mCancellationSignal != null;
-    }
-}
diff --git a/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
new file mode 100644
index 0000000..912dd95
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
@@ -0,0 +1,448 @@
+/*
+ * 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.os.AsyncTask;
+import android.os.ICancellationSignal;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.print.ILayoutResultCallback;
+import android.print.IPrintDocumentAdapter;
+import android.print.IWriteResultCallback;
+import android.print.PageRange;
+import android.print.PrintAttributes;
+import android.print.PrintDocumentAdapter.LayoutResultCallback;
+import android.print.PrintDocumentAdapter.WriteResultCallback;
+import android.print.PrintDocumentInfo;
+import android.util.Log;
+import android.util.Slog;
+
+import libcore.io.IoUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a remote print document adapter instance.
+ */
+final class RemotePrintDocumentAdapter {
+    private static final String LOG_TAG = "RemotePrintDocumentAdapter";
+
+    private static final boolean DEBUG = true;
+
+    public static final int STATE_INITIALIZED = 0;
+    public static final int STATE_START_COMPLETED = 1;
+    public static final int STATE_LAYOUT_COMPLETED = 2;
+    public static final int STATE_WRITE_COMPLETED = 3;
+    public static final int STATE_FINISH_COMPLETED = 4;
+
+    private final Object mLock = new Object();
+
+    private final List<QueuedAsyncTask> mTaskQueue = new ArrayList<QueuedAsyncTask>();
+
+    private final IPrintDocumentAdapter mRemoteInterface;
+
+    private final File mFile;
+
+    private int mState = STATE_INITIALIZED;
+
+    public RemotePrintDocumentAdapter(IPrintDocumentAdapter printAdatper, File file) {
+        mRemoteInterface = printAdatper;
+        mFile = file;
+    }
+
+    public File getFile() {
+        if (DEBUG) {
+            Log.i(LOG_TAG, "getFile()");
+        }
+        synchronized (mLock) {
+            if (mState < STATE_WRITE_COMPLETED) {
+                throw new IllegalStateException("Write not completed");
+            }
+            return mFile;
+        }
+    }
+
+    public void cancel() {
+        synchronized (mLock) {
+            final int taskCount = mTaskQueue.size();
+            for (int i = 0; i < taskCount; i++) {
+                mTaskQueue.remove(i).cancel();
+            }
+        }
+    }
+
+    public void start() {
+        QueuedAsyncTask task = new QueuedAsyncTask() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "start()");
+                }
+                synchronized (mLock) {
+                    if (mState != STATE_INITIALIZED) {
+                        throw new IllegalStateException("Invalid state: " + mState);
+                    }
+                }
+                try {
+                    mRemoteInterface.start();
+                    synchronized (mLock) {
+                        mState = STATE_START_COMPLETED;
+                    }
+                } catch (RemoteException re) {
+                    Log.e(LOG_TAG, "Error reading file", re);
+                }
+                return null;
+            }
+        };
+        synchronized (mLock) {
+            mTaskQueue.add(task);
+            task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+        }
+    }
+
+    public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+            LayoutResultCallback callback) {
+        LayoutAsyncTask task = new LayoutAsyncTask(oldAttributes, newAttributes, callback);
+        synchronized (mLock) {
+            mTaskQueue.add(task);
+            task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+        }
+    }
+
+    public void write(List<PageRange> pages, WriteResultCallback callback) {
+        WriteAsyncTask task = new WriteAsyncTask(pages, callback);
+        mTaskQueue.add(task);
+        task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+    }
+
+    public void finish() {
+        QueuedAsyncTask task = new QueuedAsyncTask() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "finish");
+                }
+                synchronized (mLock) {
+                    if (mState != STATE_LAYOUT_COMPLETED
+                            && mState != STATE_WRITE_COMPLETED) {
+                        throw new IllegalStateException("Invalid state: " + mState);
+                    }
+                }
+                try {
+                    mRemoteInterface.finish();
+                    synchronized (mLock) {
+                        mState = STATE_FINISH_COMPLETED;
+                    }
+                } catch (RemoteException re) {
+                    Log.e(LOG_TAG, "Error reading file", re);
+                    mState = STATE_INITIALIZED;
+                }
+                return null;
+            }
+        };
+        synchronized (mLock) {
+            mTaskQueue.add(task);
+            task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+        }
+    }
+
+    private abstract class QueuedAsyncTask extends AsyncTask<Void, Void, Void> {
+        public void cancel() {
+            super.cancel(true);
+        }
+    }
+
+    private final class LayoutAsyncTask extends QueuedAsyncTask {
+
+        private final PrintAttributes mOldAttributes;
+
+        private final PrintAttributes mNewAttributes;
+
+        private final LayoutResultCallback mCallback;
+
+        private final ILayoutResultCallback mILayoutResultCallback =
+                new ILayoutResultCallback.Stub() {
+            @Override
+            public void onLayoutStarted(ICancellationSignal cancellationSignal) {
+                synchronized (mLock) {
+                    mCancellationSignal = cancellationSignal;
+                    if (isCancelled()) {
+                        cancelSignalQuietlyLocked();
+                    }
+                }
+            }
+
+            @Override
+            public void onLayoutFinished(PrintDocumentInfo info, boolean changed) {
+                synchronized (mLock) {
+                    mCancellationSignal = null;
+                    mCompleted = true;
+                    mLock.notifyAll();
+                }
+                mCallback.onLayoutFinished(info, changed);
+            }
+
+            @Override
+            public void onLayoutFailed(CharSequence error) {
+                synchronized (mLock) {
+                    mCancellationSignal = null;
+                    mCompleted = true;
+                    mLock.notifyAll();
+                }
+                Slog.e(LOG_TAG, "Error laying out print document: " + error);
+                mCallback.onLayoutFailed(error);
+            }
+        };
+
+        private ICancellationSignal mCancellationSignal;
+
+        private boolean mCompleted;
+
+        public LayoutAsyncTask(PrintAttributes oldAttributes,
+                PrintAttributes newAttributes, LayoutResultCallback callback) {
+            mOldAttributes = oldAttributes;
+            mNewAttributes = newAttributes;
+            mCallback = callback;
+        }
+
+        @Override
+        public void cancel() {
+            synchronized (mLock) {
+                throwIfCancelledLocked();
+                cancelSignalQuietlyLocked();
+            }
+            super.cancel();
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            synchronized (mLock) {
+                if (mState != STATE_START_COMPLETED
+                        && mState != STATE_LAYOUT_COMPLETED
+                        && mState != STATE_WRITE_COMPLETED) {
+                    throw new IllegalStateException("Invalid state: " + mState);
+                }
+            }
+            try {
+                mRemoteInterface.layout(mOldAttributes, mNewAttributes,
+                        mILayoutResultCallback);
+                synchronized (mLock) {
+                    while (true) {
+                        if (isCancelled()) {
+                            mState = STATE_INITIALIZED;
+                            mTaskQueue.remove(this);
+                            break;
+                        }
+                        if (mCompleted) {
+                            mState = STATE_LAYOUT_COMPLETED;
+                            mTaskQueue.remove(this);
+                            break;
+                        }
+                        try {
+                            mLock.wait();
+                        } catch (InterruptedException ie) {
+                            /* ignore */
+                        }
+                    }
+                }
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error calling layout", re);
+                mState = STATE_INITIALIZED;
+            }
+            return null;
+        }
+
+        private void cancelSignalQuietlyLocked() {
+            if (mCancellationSignal != null) {
+                try {
+                    mCancellationSignal.cancel();
+                } catch (RemoteException re) {
+                    Slog.e(LOG_TAG, "Error cancelling layout", re);
+                }
+            }
+        }
+
+        private void throwIfCancelledLocked() {
+            if (isCancelled()) {
+                throw new IllegalStateException("Already cancelled");
+            }
+        }
+    }
+
+    private final class WriteAsyncTask extends QueuedAsyncTask {
+
+        private final List<PageRange> mPages;
+
+        private final WriteResultCallback mCallback;
+
+        private final IWriteResultCallback mIWriteResultCallback =
+                new IWriteResultCallback.Stub() {
+            @Override
+            public void onWriteStarted(ICancellationSignal cancellationSignal) {
+                synchronized (mLock) {
+                    mCancellationSignal = cancellationSignal;
+                    if (isCancelled()) {
+                        cancelSignalQuietlyLocked();
+                    }
+                }
+            }
+
+            @Override
+            public void onWriteFinished(List<PageRange> pages) {
+                synchronized (mLock) {
+                    mCancellationSignal = null;
+                    mCompleted = true;
+                    mLock.notifyAll();
+                }
+                mCallback.onWriteFinished(pages);
+            }
+
+            @Override
+            public void onWriteFailed(CharSequence error) {
+                synchronized (mLock) {
+                    mCancellationSignal = null;
+                    mCompleted = true;
+                    mLock.notifyAll();
+                }
+                Slog.e(LOG_TAG, "Error writing print document: " + error);
+                mCallback.onWriteFailed(error);
+            }
+        };
+
+        private ICancellationSignal mCancellationSignal;
+
+        private boolean mCompleted;
+
+        private Thread mWriteThread;
+
+        public WriteAsyncTask(List<PageRange> pages, WriteResultCallback callback) {
+            mPages = pages;
+            mCallback = callback;
+        }
+
+        @Override
+        public void cancel() {
+            synchronized (mLock) {
+                throwIfCancelledLocked();
+                cancelSignalQuietlyLocked();
+                mWriteThread.interrupt();
+            }
+            super.cancel();
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            if (DEBUG) {
+                Log.i(LOG_TAG, "print()");
+            }
+            synchronized (mLock) {
+                if (mState != STATE_LAYOUT_COMPLETED) {
+                    throw new IllegalStateException("Invalid state: " + mState);
+                }
+            }
+            InputStream in = null;
+            OutputStream out = null;
+            ParcelFileDescriptor source = null;
+            ParcelFileDescriptor sink = null;
+            synchronized (mLock) {
+                mWriteThread = Thread.currentThread();
+            }
+            try {
+                ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
+                source = pipe[0];
+                sink = pipe[1];
+
+                in = new FileInputStream(source.getFileDescriptor());
+                out = new FileOutputStream(mFile);
+
+                // Async call to initiate the other process writing the data.
+                mRemoteInterface.write(mPages, sink, mIWriteResultCallback);
+
+                // Close the source. It is now held by the client.
+                sink.close();
+                sink = null;
+
+                final byte[] buffer = new byte[8192];
+                while (true) {
+                    if (Thread.currentThread().isInterrupted()) {
+                        Thread.currentThread().interrupt();
+                        break;
+                    }
+                    final int readByteCount = in.read(buffer);
+                    if (readByteCount < 0) {
+                        break;
+                    }
+                    out.write(buffer, 0, readByteCount);
+                }
+                synchronized (mLock) {
+                    while (true) {
+                        if (isCancelled()) {
+                            mState = STATE_INITIALIZED;
+                            mTaskQueue.remove(this);
+                            break;
+                        }
+                        if (mCompleted) {
+                            mState = STATE_WRITE_COMPLETED;
+                            mTaskQueue.remove(this);
+                            break;
+                        }
+                        try {
+                            mLock.wait();
+                        } catch (InterruptedException ie) {
+                            /* ignore */
+                        }
+                    }
+                }
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error writing print document", re);
+                mState = STATE_INITIALIZED;
+            } catch (IOException ioe) {
+                Slog.e(LOG_TAG, "Error writing print document", ioe);
+                mState = STATE_INITIALIZED;
+            } finally {
+                IoUtils.closeQuietly(in);
+                IoUtils.closeQuietly(out);
+                IoUtils.closeQuietly(sink);
+                IoUtils.closeQuietly(source);
+            }
+            return null;
+        }
+
+        private void cancelSignalQuietlyLocked() {
+            if (mCancellationSignal != null) {
+                try {
+                    mCancellationSignal.cancel();
+                } catch (RemoteException re) {
+                    Slog.e(LOG_TAG, "Error cancelling layout", re);
+                }
+            }
+        }
+
+        private void throwIfCancelledLocked() {
+            if (isCancelled()) {
+                throw new IllegalStateException("Already cancelled");
+            }
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 69fc3cf..b1e38d8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -621,9 +621,9 @@
         int maxHeight =
                 mContext.getResources().getDimensionPixelSize(R.dimen.notification_max_height);
         StatusBarNotification sbn = entry.notification;
-        RemoteViews oneU = sbn.getNotification().contentView;
-        RemoteViews large = sbn.getNotification().bigContentView;
-        if (oneU == null) {
+        RemoteViews contentView = sbn.getNotification().contentView;
+        RemoteViews bigContentView = sbn.getNotification().bigContentView;
+        if (contentView == null) {
             return false;
         }
 
@@ -657,13 +657,12 @@
             content.setOnClickListener(null);
         }
 
-        // TODO(cwren) normalize variable names with those in updateNotification
-        View expandedOneU = null;
-        View expandedLarge = null;
+        View contentViewLocal = null;
+        View bigContentViewLocal = null;
         try {
-            expandedOneU = oneU.apply(mContext, adaptive, mOnClickHandler);
-            if (large != null) {
-                expandedLarge = large.apply(mContext, adaptive, mOnClickHandler);
+            contentViewLocal = contentView.apply(mContext, adaptive, mOnClickHandler);
+            if (bigContentView != null) {
+                bigContentViewLocal = bigContentView.apply(mContext, adaptive, mOnClickHandler);
             }
         }
         catch (RuntimeException e) {
@@ -672,26 +671,24 @@
             return false;
         }
 
-        if (expandedOneU != null) {
+        if (contentViewLocal != null) {
             SizeAdaptiveLayout.LayoutParams params =
-                    new SizeAdaptiveLayout.LayoutParams(expandedOneU.getLayoutParams());
+                    new SizeAdaptiveLayout.LayoutParams(contentViewLocal.getLayoutParams());
             params.minHeight = minHeight;
             params.maxHeight = minHeight;
-            adaptive.addView(expandedOneU, params);
+            adaptive.addView(contentViewLocal, params);
         }
-        if (expandedLarge != null) {
+        if (bigContentViewLocal != null) {
             SizeAdaptiveLayout.LayoutParams params =
-                    new SizeAdaptiveLayout.LayoutParams(expandedLarge.getLayoutParams());
+                    new SizeAdaptiveLayout.LayoutParams(bigContentViewLocal.getLayoutParams());
             params.minHeight = minHeight+1;
             params.maxHeight = maxHeight;
-            adaptive.addView(expandedLarge, params);
+            adaptive.addView(bigContentViewLocal, params);
         }
         row.setDrawingCacheEnabled(true);
 
         applyLegacyRowBackground(sbn, content);
 
-        row.setTag(R.id.expandable_tag, Boolean.valueOf(large != null));
-
         if (MULTIUSER_DEBUG) {
             TextView debug = (TextView) row.findViewById(R.id.debug_info);
             if (debug != null) {
@@ -701,8 +698,8 @@
         }
         entry.row = row;
         entry.content = content;
-        entry.expanded = expandedOneU;
-        entry.setLargeView(expandedLarge);
+        entry.expanded = contentViewLocal;
+        entry.setBigContentView(bigContentViewLocal);
 
         return true;
     }
@@ -944,8 +941,8 @@
                 && oldContentView.getLayoutId() == contentView.getLayoutId();
         // large view may be null
         boolean bigContentsUnchanged =
-                (oldEntry.getLargeView() == null && bigContentView == null)
-                || ((oldEntry.getLargeView() != null && bigContentView != null)
+                (oldEntry.getBigContentView() == null && bigContentView == null)
+                || ((oldEntry.getBigContentView() != null && bigContentView != null)
                     && bigContentView.getPackage() != null
                     && oldBigContentView.getPackage() != null
                     && oldBigContentView.getPackage().equals(bigContentView.getPackage())
@@ -965,8 +962,8 @@
             try {
                 // Reapply the RemoteViews
                 contentView.reapply(mContext, oldEntry.expanded, mOnClickHandler);
-                if (bigContentView != null && oldEntry.getLargeView() != null) {
-                    bigContentView.reapply(mContext, oldEntry.getLargeView(), mOnClickHandler);
+                if (bigContentView != null && oldEntry.getBigContentView() != null) {
+                    bigContentView.reapply(mContext, oldEntry.getBigContentView(), mOnClickHandler);
                 }
                 // update the contentIntent
                 final PendingIntent contentIntent = notification.getNotification().contentIntent;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index 673ca6b..8f62ebf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -38,19 +38,19 @@
         public View content; // takes the click events and sends the PendingIntent
         public View expanded; // the inflated RemoteViews
         public ImageView largeIcon;
-        protected View expandedLarge;
+        private View expandedBig;
         public Entry() {}
         public Entry(IBinder key, StatusBarNotification n, StatusBarIconView ic) {
             this.key = key;
             this.notification = n;
             this.icon = ic;
         }
-        public void setLargeView(View expandedLarge) {
-            this.expandedLarge = expandedLarge;
-            writeBooleanTag(row, R.id.expandable_tag, expandedLarge != null);
+        public void setBigContentView(View bigContentView) {
+            this.expandedBig = bigContentView;
+            writeBooleanTag(row, R.id.expandable_tag, bigContentView != null);
         }
-        public View getLargeView() {
-            return expandedLarge;
+        public View getBigContentView() {
+            return expandedBig;
         }
         /**
          * Return whether the entry can be expanded.
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index 5b76f39..203cca6 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -70,7 +70,7 @@
         mAppWidgetServices.append(0, primary);
     }
 
-    public void systemReady(boolean safeMode) {
+    public void systemRunning(boolean safeMode) {
         mSafeMode = safeMode;
 
         mAppWidgetServices.get(0).systemReady(safeMode);
diff --git a/services/java/com/android/server/AssetAtlasService.java b/services/java/com/android/server/AssetAtlasService.java
index 33f082c..26b4652 100644
--- a/services/java/com/android/server/AssetAtlasService.java
+++ b/services/java/com/android/server/AssetAtlasService.java
@@ -186,7 +186,7 @@
      * Callback invoked by the server thread to indicate we can now run
      * 3rd party code.
      */
-    public void systemReady() {
+    public void systemRunning() {
     }
 
     /**
diff --git a/services/java/com/android/server/CommonTimeManagementService.java b/services/java/com/android/server/CommonTimeManagementService.java
index c316733..aa2c8b8 100644
--- a/services/java/com/android/server/CommonTimeManagementService.java
+++ b/services/java/com/android/server/CommonTimeManagementService.java
@@ -153,7 +153,7 @@
         mContext = context;
     }
 
-    void systemReady() {
+    void systemRunning() {
         if (ServiceManager.checkService(CommonTimeConfig.SERVICE_NAME) == null) {
             Log.i(TAG, "No common time service detected on this platform.  " +
                        "Common time services will be unavailable.");
diff --git a/services/java/com/android/server/CountryDetectorService.java b/services/java/com/android/server/CountryDetectorService.java
index 8407fa4..4956dd5 100644
--- a/services/java/com/android/server/CountryDetectorService.java
+++ b/services/java/com/android/server/CountryDetectorService.java
@@ -166,7 +166,7 @@
         }
     }
 
-    void systemReady() {
+    void systemRunning() {
         // Shall we wait for the initialization finish.
         BackgroundThread.getHandler().post(this);
     }
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 1c1b002..35656f8 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -823,7 +823,7 @@
         }
     }
 
-    public void systemReady(StatusBarManagerService statusBar) {
+    public void systemRunning(StatusBarManagerService statusBar) {
         synchronized (mMethodMap) {
             if (DEBUG) {
                 Slog.d(TAG, "--- systemReady");
@@ -1210,7 +1210,7 @@
         mCurIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
                 mContext, 0, new Intent(Settings.ACTION_INPUT_METHOD_SETTINGS), 0));
         if (bindCurrentInputMethodService(mCurIntent, this, Context.BIND_AUTO_CREATE
-                | Context.BIND_NOT_VISIBLE)) {
+                | Context.BIND_NOT_VISIBLE | Context.BIND_SHOWING_UI)) {
             mLastBindTime = SystemClock.uptimeMillis();
             mHaveConnection = true;
             mCurId = info.getId();
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index c6c9845..bde9e1c 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -198,7 +198,7 @@
         // most startup is deferred until systemReady()
     }
 
-    public void systemReady() {
+    public void systemRunning() {
         synchronized (mLock) {
             if (D) Log.d(TAG, "systemReady()");
 
diff --git a/services/java/com/android/server/NetworkTimeUpdateService.java b/services/java/com/android/server/NetworkTimeUpdateService.java
index 02b42b8..cbddf67 100644
--- a/services/java/com/android/server/NetworkTimeUpdateService.java
+++ b/services/java/com/android/server/NetworkTimeUpdateService.java
@@ -108,7 +108,7 @@
     }
 
     /** Initialize the receivers and initiate the first NTP request */
-    public void systemReady() {
+    public void systemRunning() {
         registerForTelephonyIntents();
         registerForAlarms();
         registerForConnectivityIntents();
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 888064c..0bbdcfb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -351,6 +351,7 @@
         LockSettingsService lockSettings = null;
         DreamManagerService dreamy = null;
         AssetAtlasService atlas = null;
+        PrintManagerService printManager = null;
 
         // Bring up services needed for UI.
         if (factoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) {
@@ -793,8 +794,8 @@
 
             try {
                 Slog.i(TAG, "Print Service");
-                ServiceManager.addService(Context.PRINT_SERVICE,
-                        new PrintManagerService(context));
+                printManager = new PrintManagerService(context);
+                ServiceManager.addService(Context.PRINT_SERVICE, printManager);
             } catch (Throwable e) {
                 reportWtf("starting Print Service", e);
             }
@@ -909,6 +910,7 @@
         final AssetAtlasService atlasF = atlas;
         final InputManagerService inputManagerF = inputManager;
         final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
+        final PrintManagerService printManagerF = printManager;
 
         // We now tell the activity manager it is okay to run third party
         // code.  It will call back into us once it has gotten to the state
@@ -988,66 +990,73 @@
                 // third party code...
 
                 try {
-                    if (appWidgetF != null) appWidgetF.systemReady(safeMode);
+                    if (appWidgetF != null) appWidgetF.systemRunning(safeMode);
                 } catch (Throwable e) {
-                    reportWtf("making App Widget Service ready", e);
+                    reportWtf("Notifying AppWidgetService running", e);
                 }
                 try {
-                    if (wallpaperF != null) wallpaperF.systemReady();
+                    if (wallpaperF != null) wallpaperF.systemRunning();
                 } catch (Throwable e) {
-                    reportWtf("making Wallpaper Service ready", e);
+                    reportWtf("Notifying WallpaperService running", e);
                 }
                 try {
-                    if (immF != null) immF.systemReady(statusBarF);
+                    if (immF != null) immF.systemRunning(statusBarF);
                 } catch (Throwable e) {
-                    reportWtf("making Input Method Service ready", e);
+                    reportWtf("Notifying InputMethodService running", e);
                 }
                 try {
-                    if (locationF != null) locationF.systemReady();
+                    if (locationF != null) locationF.systemRunning();
                 } catch (Throwable e) {
-                    reportWtf("making Location Service ready", e);
+                    reportWtf("Notifying Location Service running", e);
                 }
                 try {
-                    if (countryDetectorF != null) countryDetectorF.systemReady();
+                    if (countryDetectorF != null) countryDetectorF.systemRunning();
                 } catch (Throwable e) {
-                    reportWtf("making Country Detector Service ready", e);
+                    reportWtf("Notifying CountryDetectorService running", e);
                 }
                 try {
-                    if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemReady();
+                    if (networkTimeUpdaterF != null) networkTimeUpdaterF.systemRunning();
                 } catch (Throwable e) {
-                    reportWtf("making Network Time Service ready", e);
+                    reportWtf("Notifying NetworkTimeService running", e);
                 }
                 try {
-                    if (commonTimeMgmtServiceF != null) commonTimeMgmtServiceF.systemReady();
+                    if (commonTimeMgmtServiceF != null) commonTimeMgmtServiceF.systemRunning();
                 } catch (Throwable e) {
-                    reportWtf("making Common time management service ready", e);
+                    reportWtf("Notifying CommonTimeManagementService running", e);
                 }
                 try {
-                    if (textServiceManagerServiceF != null) textServiceManagerServiceF.systemReady();
+                    if (textServiceManagerServiceF != null)
+                        textServiceManagerServiceF.systemRunning();
                 } catch (Throwable e) {
-                    reportWtf("making Text Services Manager Service ready", e);
+                    reportWtf("Notifying TextServicesManagerService running", e);
                 }
                 try {
-                    if (dreamyF != null) dreamyF.systemReady();
+                    if (dreamyF != null) dreamyF.systemRunning();
                 } catch (Throwable e) {
-                    reportWtf("making DreamManagerService ready", e);
+                    reportWtf("Notifying DreamManagerService running", e);
                 }
                 try {
-                    if (atlasF != null) atlasF.systemReady();
+                    if (atlasF != null) atlasF.systemRunning();
                 } catch (Throwable e) {
-                    reportWtf("making AssetAtlasService ready", e);
+                    reportWtf("Notifying AssetAtlasService running", e);
                 }
                 try {
                     // TODO(BT) Pass parameter to input manager
-                    if (inputManagerF != null) inputManagerF.systemReady();
+                    if (inputManagerF != null) inputManagerF.systemRunning();
                 } catch (Throwable e) {
-                    reportWtf("making InputManagerService ready", e);
+                    reportWtf("Notifying InputManagerService running", e);
                 }
 
                 try {
-                    if (telephonyRegistryF != null) telephonyRegistryF.systemReady();
+                    if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
                 } catch (Throwable e) {
-                    reportWtf("making TelephonyRegistry ready", e);
+                    reportWtf("Notifying TelephonyRegistry running", e);
+                }
+
+                try {
+                    if (printManagerF != null) printManagerF.systemRuning();
+                } catch (Throwable e) {
+                    reportWtf("Notifying PrintManagerService running", e);
                 }
             }
         });
diff --git a/services/java/com/android/server/TelephonyRegistry.java b/services/java/com/android/server/TelephonyRegistry.java
index 17260d5..699d79e 100644
--- a/services/java/com/android/server/TelephonyRegistry.java
+++ b/services/java/com/android/server/TelephonyRegistry.java
@@ -178,7 +178,7 @@
         mConnectedApns = new ArrayList<String>();
     }
 
-    public void systemReady() {
+    public void systemRunning() {
         // Watch for interesting updates
         final IntentFilter filter = new IntentFilter();
         filter.addAction(Intent.ACTION_USER_SWITCHED);
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index 7dd9988..6587c41 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -76,7 +76,7 @@
             new HashMap<String, SpellCheckerBindGroup>();
     private final TextServicesSettings mSettings;
 
-    public void systemReady() {
+    public void systemRunning() {
         if (!mSystemReady) {
             mSystemReady = true;
         }
diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java
index 6823f136..d677f24 100644
--- a/services/java/com/android/server/WallpaperManagerService.java
+++ b/services/java/com/android/server/WallpaperManagerService.java
@@ -449,7 +449,7 @@
         }
     }
 
-    public void systemReady() {
+    public void systemRunning() {
         if (DEBUG) Slog.v(TAG, "systemReady");
         WallpaperData wallpaper = mWallpaperMap.get(UserHandle.USER_OWNER);
         switchWallpaper(wallpaper, null);
@@ -885,7 +885,8 @@
                     Intent.createChooser(new Intent(Intent.ACTION_SET_WALLPAPER),
                             mContext.getText(com.android.internal.R.string.chooser_wallpaper)),
                     0, null, new UserHandle(serviceUserId)));
-            if (!mContext.bindServiceAsUser(intent, newConn, Context.BIND_AUTO_CREATE,
+            if (!mContext.bindServiceAsUser(intent, newConn,
+                    Context.BIND_AUTO_CREATE | Context.BIND_SHOWING_UI,
                     new UserHandle(serviceUserId))) {
                 String msg = "Unable to bind service: "
                         + componentName;
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 5d72102..795e142 100644
--- a/services/java/com/android/server/am/ActiveServices.java
+++ b/services/java/com/android/server/am/ActiveServices.java
@@ -421,7 +421,8 @@
 
     private void updateServiceForegroundLocked(ProcessRecord proc, boolean oomAdj) {
         boolean anyForeground = false;
-        for (ServiceRecord sr : proc.services) {
+        for (int i=proc.services.size()-1; i>=0; i--) {
+            ServiceRecord sr = proc.services.valueAt(i);
             if (sr.isForeground) {
                 anyForeground = true;
                 break;
@@ -1670,78 +1671,72 @@
         }
 
         // Clean up any connections this application has to other services.
-        if (app.connections.size() > 0) {
-            Iterator<ConnectionRecord> it = app.connections.iterator();
-            while (it.hasNext()) {
-                ConnectionRecord r = it.next();
-                removeConnectionLocked(r, app, null);
-            }
+        for (int i=app.connections.size()-1; i>=0; i--) {
+            ConnectionRecord r = app.connections.valueAt(i);
+            removeConnectionLocked(r, app, null);
         }
         app.connections.clear();
 
-        if (app.services.size() != 0) {
+        for (int i=app.services.size()-1; i>=0; i--) {
             // Any services running in the application need to be placed
             // back in the pending list.
-            Iterator<ServiceRecord> it = app.services.iterator();
-            while (it.hasNext()) {
-                ServiceRecord sr = it.next();
-                synchronized (sr.stats.getBatteryStats()) {
-                    sr.stats.stopLaunchedLocked();
-                }
-                sr.app = null;
-                sr.isolatedProc = null;
-                sr.executeNesting = 0;
-                if (sr.tracker != null) {
-                    sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(),
-                            SystemClock.uptimeMillis());
-                }
-                if (mStoppingServices.remove(sr)) {
-                    if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
-                }
+            ServiceRecord sr = app.services.valueAt(i);
+            synchronized (sr.stats.getBatteryStats()) {
+                sr.stats.stopLaunchedLocked();
+            }
+            sr.app = null;
+            sr.isolatedProc = null;
+            sr.executeNesting = 0;
+            if (sr.tracker != null) {
+                sr.tracker.setExecuting(false, mAm.mProcessTracker.getMemFactorLocked(),
+                        SystemClock.uptimeMillis());
+            }
+            if (mStoppingServices.remove(sr)) {
+                if (DEBUG_SERVICE) Slog.v(TAG, "killServices remove stopping " + sr);
+            }
 
-                final int numClients = sr.bindings.size();
-                for (int bindingi=numClients-1; bindingi>=0; bindingi--) {
-                    IntentBindRecord b = sr.bindings.valueAt(bindingi);
-                    if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b
-                            + ": shouldUnbind=" + b.hasBound);
-                    b.binder = null;
-                    b.requested = b.received = b.hasBound = false;
-                }
+            final int numClients = sr.bindings.size();
+            for (int bindingi=numClients-1; bindingi>=0; bindingi--) {
+                IntentBindRecord b = sr.bindings.valueAt(bindingi);
+                if (DEBUG_SERVICE) Slog.v(TAG, "Killing binding " + b
+                        + ": shouldUnbind=" + b.hasBound);
+                b.binder = null;
+                b.requested = b.received = b.hasBound = false;
+            }
 
-                if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
-                        &ApplicationInfo.FLAG_PERSISTENT) == 0) {
-                    Slog.w(TAG, "Service crashed " + sr.crashCount
-                            + " times, stopping: " + sr);
-                    EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
-                            sr.userId, sr.crashCount, sr.shortName, app.pid);
-                    bringDownServiceLocked(sr);
-                } else if (!allowRestart) {
-                    bringDownServiceLocked(sr);
-                } else {
-                    boolean canceled = scheduleServiceRestartLocked(sr, true);
+            if (sr.crashCount >= 2 && (sr.serviceInfo.applicationInfo.flags
+                    &ApplicationInfo.FLAG_PERSISTENT) == 0) {
+                Slog.w(TAG, "Service crashed " + sr.crashCount
+                        + " times, stopping: " + sr);
+                EventLog.writeEvent(EventLogTags.AM_SERVICE_CRASHED_TOO_MUCH,
+                        sr.userId, sr.crashCount, sr.shortName, app.pid);
+                bringDownServiceLocked(sr);
+            } else if (!allowRestart) {
+                bringDownServiceLocked(sr);
+            } else {
+                boolean canceled = scheduleServiceRestartLocked(sr, true);
 
-                    // Should the service remain running?  Note that in the
-                    // extreme case of so many attempts to deliver a command
-                    // that it failed we also will stop it here.
-                    if (sr.startRequested && (sr.stopIfKilled || canceled)) {
-                        if (sr.pendingStarts.size() == 0) {
-                            sr.startRequested = false;
-                            if (sr.tracker != null) {
-                                sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
-                                        SystemClock.uptimeMillis());
-                            }
-                            if (!sr.hasAutoCreateConnections()) {
-                                // Whoops, no reason to restart!
-                                bringDownServiceLocked(sr);
-                            }
+                // Should the service remain running?  Note that in the
+                // extreme case of so many attempts to deliver a command
+                // that it failed we also will stop it here.
+                if (sr.startRequested && (sr.stopIfKilled || canceled)) {
+                    if (sr.pendingStarts.size() == 0) {
+                        sr.startRequested = false;
+                        if (sr.tracker != null) {
+                            sr.tracker.setStarted(false, mAm.mProcessTracker.getMemFactorLocked(),
+                                    SystemClock.uptimeMillis());
+                        }
+                        if (!sr.hasAutoCreateConnections()) {
+                            // Whoops, no reason to restart!
+                            bringDownServiceLocked(sr);
                         }
                     }
                 }
             }
+        }
 
-            if (!allowRestart) {
-                app.services.clear();
-            }
+        if (!allowRestart) {
+            app.services.clear();
         }
 
         // Make sure we have no more records on the stopping list.
@@ -1880,11 +1875,10 @@
                 return;
             }
             long maxTime = SystemClock.uptimeMillis() - SERVICE_TIMEOUT;
-            Iterator<ServiceRecord> it = proc.executingServices.iterator();
             ServiceRecord timeout = null;
             long nextTime = 0;
-            while (it.hasNext()) {
-                ServiceRecord sr = it.next();
+            for (int i=proc.executingServices.size()-1; i>=0; i--) {
+                ServiceRecord sr = proc.executingServices.valueAt(i);
                 if (sr.executingStart < maxTime) {
                     timeout = sr;
                     break;
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c41c23e..b51415f 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1505,6 +1505,7 @@
             case COLLECT_PSS_BG_MSG: {
                 int i=0;
                 long start = SystemClock.uptimeMillis();
+                long[] tmp = new long[1];
                 do {
                     ProcessRecord proc;
                     int oomAdj;
@@ -1528,10 +1529,10 @@
                         i++;
                     }
                     if (proc != null) {
-                        long pss = Debug.getPss(pid);
+                        long pss = Debug.getPss(pid, tmp);
                         synchronized (ActivityManagerService.this) {
                             if (proc.thread != null && proc.setAdj == oomAdj && proc.pid == pid) {
-                                proc.baseProcessTracker.addPss(pss, true);
+                                proc.baseProcessTracker.addPss(pss, tmp[0], true);
                             }
                         }
                     }
@@ -2157,13 +2158,12 @@
 
         // If the app is currently using a content provider or service,
         // bump those processes as well.
-        if (app.connections.size() > 0) {
-            for (ConnectionRecord cr : app.connections) {
-                if (cr.binding != null && cr.binding.service != null
-                        && cr.binding.service.app != null
-                        && cr.binding.service.app.lruSeq != mLruSeq) {
-                    updateLruProcessInternalLocked(cr.binding.service.app, i+1);
-                }
+        for (int j=app.connections.size()-1; j>=0; j--) {
+            ConnectionRecord cr = app.connections.valueAt(j);
+            if (cr.binding != null && cr.binding.service != null
+                    && cr.binding.service.app != null
+                    && cr.binding.service.app.lruSeq != mLruSeq) {
+                updateLruProcessInternalLocked(cr.binding.service.app, i+1);
             }
         }
         for (int j=app.conProviders.size()-1; j>=0; j--) {
@@ -4010,7 +4010,8 @@
                 synchronized (this) {
                     if (proc.thread != null && proc.setAdj == oomAdj) {
                         // Record this for posterity if the process has been stable.
-                        proc.baseProcessTracker.addPss(infos[i].getTotalPss(), false);
+                        proc.baseProcessTracker.addPss(infos[i].getTotalPss(),
+                                infos[i].getTotalUss(), false);
                     }
                 }
             }
@@ -4031,12 +4032,13 @@
                     oomAdj = proc != null ? proc.setAdj : 0;
                 }
             }
-            pss[i] = Debug.getPss(pids[i]);
+            long[] tmpUss = new long[1];
+            pss[i] = Debug.getPss(pids[i], tmpUss);
             if (proc != null) {
                 synchronized (this) {
                     if (proc.thread != null && proc.setAdj == oomAdj) {
                         // Record this for posterity if the process has been stable.
-                        proc.baseProcessTracker.addPss(pss[i], false);
+                        proc.baseProcessTracker.addPss(pss[i], tmpUss[0], false);
                     }
                 }
             }
@@ -8802,14 +8804,11 @@
         }
 
         // Bump up the crash count of any services currently running in the proc.
-        if (app.services.size() != 0) {
+        for (int i=app.services.size()-1; i>=0; i--) {
             // Any services running in the application need to be placed
             // back in the pending list.
-            Iterator<ServiceRecord> it = app.services.iterator();
-            while (it.hasNext()) {
-                ServiceRecord sr = it.next();
-                sr.crashCount++;
-            }
+            ServiceRecord sr = app.services.valueAt(i);
+            sr.crashCount++;
         }
 
         // If the crashing process is what we consider to be the "home process" and it has been
@@ -10269,8 +10268,8 @@
             pw.print("    FOREGROUND_APP_ADJ: "); pw.println(ProcessList.FOREGROUND_APP_ADJ);
             pw.print("    VISIBLE_APP_ADJ: "); pw.println(ProcessList.VISIBLE_APP_ADJ);
             pw.print("    PERCEPTIBLE_APP_ADJ: "); pw.println(ProcessList.PERCEPTIBLE_APP_ADJ);
-            pw.print("    HEAVY_WEIGHT_APP_ADJ: "); pw.println(ProcessList.HEAVY_WEIGHT_APP_ADJ);
             pw.print("    BACKUP_APP_ADJ: "); pw.println(ProcessList.BACKUP_APP_ADJ);
+            pw.print("    HEAVY_WEIGHT_APP_ADJ: "); pw.println(ProcessList.HEAVY_WEIGHT_APP_ADJ);
             pw.print("    SERVICE_ADJ: "); pw.println(ProcessList.SERVICE_ADJ);
             pw.print("    HOME_APP_ADJ: "); pw.println(ProcessList.HOME_APP_ADJ);
             pw.print("    PREVIOUS_APP_ADJ: "); pw.println(ProcessList.PREVIOUS_APP_ADJ);
@@ -10772,10 +10771,10 @@
                 oomAdj = buildOomTag("home ", null, r.setAdj, ProcessList.HOME_APP_ADJ);
             } else if (r.setAdj >= ProcessList.SERVICE_ADJ) {
                 oomAdj = buildOomTag("svc  ", null, r.setAdj, ProcessList.SERVICE_ADJ);
-            } else if (r.setAdj >= ProcessList.BACKUP_APP_ADJ) {
-                oomAdj = buildOomTag("bkup ", null, r.setAdj, ProcessList.BACKUP_APP_ADJ);
             } else if (r.setAdj >= ProcessList.HEAVY_WEIGHT_APP_ADJ) {
                 oomAdj = buildOomTag("hvy  ", null, r.setAdj, ProcessList.HEAVY_WEIGHT_APP_ADJ);
+            } else if (r.setAdj >= ProcessList.BACKUP_APP_ADJ) {
+                oomAdj = buildOomTag("bkup ", null, r.setAdj, ProcessList.BACKUP_APP_ADJ);
             } else if (r.setAdj >= ProcessList.PERCEPTIBLE_APP_ADJ) {
                 oomAdj = buildOomTag("prcp ", null, r.setAdj, ProcessList.PERCEPTIBLE_APP_ADJ);
             } else if (r.setAdj >= ProcessList.VISIBLE_APP_ADJ) {
@@ -10820,29 +10819,38 @@
                 case ActivityManager.PROCESS_STATE_TOP:
                     procState = "T ";
                     break;
-                case ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE:
-                    procState = "IP";
+                case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
+                    procState = "IF";
                     break;
                 case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
                     procState = "IB";
                     break;
-                case ActivityManager.PROCESS_STATE_RECEIVER:
-                    procState = "R ";
-                    break;
                 case ActivityManager.PROCESS_STATE_BACKUP:
                     procState = "BU";
                     break;
+                case ActivityManager.PROCESS_STATE_HEAVY_WEIGHT:
+                    procState = "HW";
+                    break;
                 case ActivityManager.PROCESS_STATE_SERVICE:
                     procState = "S ";
                     break;
+                case ActivityManager.PROCESS_STATE_RECEIVER:
+                    procState = "R ";
+                    break;
                 case ActivityManager.PROCESS_STATE_HOME:
-                    procState = "H ";
+                    procState = "HO";
                     break;
                 case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
                     procState = "LA";
                     break;
-                case ActivityManager.PROCESS_STATE_CACHED:
-                    procState = "C ";
+                case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+                    procState = "CA";
+                    break;
+                case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                    procState = "Ca";
+                    break;
+                case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
+                    procState = "CE";
                     break;
                 default:
                     procState = "??";
@@ -10895,9 +10903,6 @@
                 pw.print(prefix);
                 pw.print("    ");
                 pw.print("oom: max="); pw.print(r.maxAdj);
-                pw.print(" cached="); pw.print(r.cachedAdj);
-                pw.print(" client="); pw.print(r.clientCachedAdj);
-                pw.print(" empty="); pw.print(r.emptyAdj);
                 pw.print(" curRaw="); pw.print(r.curRawAdj);
                 pw.print(" setRaw="); pw.print(r.setRawAdj);
                 pw.print(" cur="); pw.print(r.curAdj);
@@ -11139,21 +11144,24 @@
 
     static final int[] DUMP_MEM_OOM_ADJ = new int[] {
             ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ,
-            ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
-            ProcessList.BACKUP_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ,
+            ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ,
+            ProcessList.BACKUP_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ,
+            ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ,
             ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.CACHED_APP_MAX_ADJ
     };
     static final String[] DUMP_MEM_OOM_LABEL = new String[] {
             "System", "Persistent", "Foreground",
-            "Visible", "Perceptible", "Heavy Weight",
-            "Backup", "A Services", "Home", "Previous",
-            "B Services", "Cached"
+            "Visible", "Perceptible",
+            "Heavy Weight", "Backup",
+            "A Services", "Home",
+            "Previous", "B Services", "Cached"
     };
     static final String[] DUMP_MEM_OOM_COMPACT_LABEL = new String[] {
             "sys", "pers", "fore",
-            "vis", "percept", "heavy",
-            "backup", "servicea", "home", "prev",
-            "serviceb", "cached"
+            "vis", "percept",
+            "heavy", "backup",
+            "servicea", "home",
+            "prev", "serviceb", "cached"
     };
 
     final void dumpApplicationMemoryUsage(FileDescriptor fd,
@@ -11225,6 +11233,7 @@
         long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length];
         ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])
                 new ArrayList[DUMP_MEM_OOM_LABEL.length];
+        final long[] tmpLong = new long[1];
 
         long totalPss = 0;
         long cachedPss = 0;
@@ -11264,16 +11273,18 @@
                     if (!brief && !oomOnly) {
                         Debug.getMemoryInfo(pid, mi);
                     } else {
-                        mi.dalvikPss = (int)Debug.getPss(pid);
+                        mi.dalvikPss = (int)Debug.getPss(pid, tmpLong);
+                        mi.dalvikPrivateDirty = (int)tmpLong[0];
                     }
                 }
 
                 final long myTotalPss = mi.getTotalPss();
+                final long myTotalUss = mi.getTotalUss();
 
                 synchronized (this) {
                     if (r.thread != null && oomAdj == r.getSetAdjWithServices()) {
                         // Record this for posterity if the process has been stable.
-                        r.baseProcessTracker.addPss(myTotalPss, true);
+                        r.baseProcessTracker.addPss(myTotalPss, myTotalUss, true);
                     }
                 }
 
@@ -11642,14 +11653,11 @@
         skipCurrentReceiverLocked(app);
 
         // Unregister any receivers.
-        if (app.receivers.size() > 0) {
-            Iterator<ReceiverList> it = app.receivers.iterator();
-            while (it.hasNext()) {
-                removeReceiverLocked(it.next());
-            }
-            app.receivers.clear();
+        for (int i=app.receivers.size()-1; i>=0; i--) {
+            removeReceiverLocked(app.receivers.valueAt(i));
         }
-        
+        app.receivers.clear();
+
         // If the app is undergoing backup, tell the backup manager about it
         if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
             if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG, "App "
@@ -13409,29 +13417,17 @@
         return null;
     }
 
-    private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, int clientCachedAdj,
-            int emptyAdj, ProcessRecord TOP_APP, boolean recursed, boolean doingAll) {
+    private final int computeOomAdjLocked(ProcessRecord app, int cachedAdj, ProcessRecord TOP_APP,
+            boolean doingAll, long now) {
         if (mAdjSeq == app.adjSeq) {
-            // This adjustment has already been computed.  If we are calling
-            // from the top, we may have already computed our adjustment with
-            // an earlier cached adjustment that isn't really for us... if
-            // so, use the new cached adjustment.
-            if (!recursed && app.cached) {
-                if (app.hasActivities) {
-                    app.curAdj = app.curRawAdj = app.nonStoppingAdj = cachedAdj;
-                } else if (app.hasClientActivities) {
-                    app.curAdj = app.curRawAdj = app.nonStoppingAdj = clientCachedAdj;
-                } else {
-                    app.curAdj = app.curRawAdj = app.nonStoppingAdj = emptyAdj;
-                }
-            }
+            // This adjustment has already been computed.
             return app.curRawAdj;
         }
 
         if (app.thread == null) {
             app.adjSeq = mAdjSeq;
             app.curSchedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.curProcState = ActivityManager.PROCESS_STATE_CACHED;
+            app.curProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
             return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
         }
 
@@ -13449,7 +13445,7 @@
             // below foreground, so it is not worth doing work for it.
             app.adjType = "fixed";
             app.adjSeq = mAdjSeq;
-            app.curRawAdj = app.nonStoppingAdj = app.maxAdj;
+            app.curRawAdj = app.maxAdj;
             app.hasActivities = false;
             app.foregroundActivities = false;
             app.keeping = true;
@@ -13507,7 +13503,7 @@
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "instrumentation";
             interesting = true;
-            procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+            procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
         } else if ((queue = isReceivingBroadcast(app)) != null) {
             // An app that is currently receiving a broadcast also
             // counts as being in the foreground for OOM killer purposes.
@@ -13526,33 +13522,39 @@
             app.adjType = "exec-service";
             procState = ActivityManager.PROCESS_STATE_SERVICE;
         } else {
-            // Assume process is cached (has activities); we will correct
-            // later if this is not the case.
-            adj = cachedAdj;
+            // As far as we know the process is empty.  We may change our mind later.
             schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+            // At this point we don't actually know the adjustment.  Use the cached adj
+            // value that the caller wants us to.
+            adj = cachedAdj;
+            procState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
             app.cached = true;
-            app.adjType = "cch-act";
-            procState = ActivityManager.PROCESS_STATE_CACHED;
+            app.empty = true;
+            app.adjType = "cch-empty";
         }
 
-        boolean hasStoppingActivities = false;
-
         // Examine all activities if not already foreground.
         if (!foregroundActivities && activitiesSize > 0) {
             for (int j = 0; j < activitiesSize; j++) {
                 final ActivityRecord r = app.activities.get(j);
+                if (r.app != app) {
+                    Slog.w(TAG, "Wtf, activity " + r + " in proc activity list not using proc "
+                            + app + "?!?");
+                    continue;
+                }
+                app.hasActivities = true;
                 if (r.visible) {
                     // App has a visible activity; only upgrade adjustment.
                     if (adj > ProcessList.VISIBLE_APP_ADJ) {
                         adj = ProcessList.VISIBLE_APP_ADJ;
                         app.adjType = "visible";
                     }
-                    if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
-                        procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                    if (procState > ActivityManager.PROCESS_STATE_TOP) {
+                        procState = ActivityManager.PROCESS_STATE_TOP;
                     }
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
                     app.cached = false;
-                    app.hasActivities = true;
+                    app.empty = false;
                     foregroundActivities = true;
                     break;
                 } else if (r.state == ActivityState.PAUSING || r.state == ActivityState.PAUSED) {
@@ -13560,38 +13562,39 @@
                         adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                         app.adjType = "pausing";
                     }
-                    if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
-                        procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                    if (procState > ActivityManager.PROCESS_STATE_TOP) {
+                        procState = ActivityManager.PROCESS_STATE_TOP;
                     }
+                    schedGroup = Process.THREAD_GROUP_DEFAULT;
                     app.cached = false;
+                    app.empty = false;
                     foregroundActivities = true;
                 } else if (r.state == ActivityState.STOPPING) {
-                    // We will apply the actual adjustment later, because
-                    // we want to allow this process to immediately go through
-                    // any memory trimming that is in effect.
-                    if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
-                        procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+                    if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                        adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                        app.adjType = "stopping";
+                    }
+                    // For the process state, we will at this point consider the
+                    // process to be cached.  It will be cached either as an activity
+                    // or empty depending on whether the activity is finishing.  We do
+                    // this so that we can treat the process as cached for purposes of
+                    // memory trimming (determing current memory level, trim command to
+                    // send to process) since there can be an arbitrary number of stopping
+                    // processes and they should soon all go into the cached state.
+                    if (!r.finishing) {
+                        if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+                            procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+                        }
                     }
                     app.cached = false;
+                    app.empty = false;
                     foregroundActivities = true;
-                    hasStoppingActivities = true;
+                } else {
+                    if (procState > ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+                        procState = ActivityManager.PROCESS_STATE_CACHED_ACTIVITY;
+                        app.adjType = "cch-act";
+                    }
                 }
-                if (r.app == app) {
-                    app.hasActivities = true;
-                }
-            }
-        }
-
-        if (adj == cachedAdj && !app.hasActivities) {
-            if (app.hasClientActivities) {
-                adj = clientCachedAdj;
-                app.adjType = "cch-client-act";
-            } else {
-                // Whoops, this process is completely empty as far as we know
-                // at this point.
-                adj = emptyAdj;
-                app.empty = true;
-                app.adjType = "cch-empty";
             }
         }
 
@@ -13599,14 +13602,14 @@
             if (app.foregroundServices) {
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
                 app.cached = false;
                 app.adjType = "fg-service";
                 schedGroup = Process.THREAD_GROUP_DEFAULT;
             } else if (app.forcingToForeground != null) {
                 // The user is aware of this app, so make it visible.
                 adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
                 app.cached = false;
                 app.adjType = "force-fg";
                 app.adjSource = app.forcingToForeground;
@@ -13618,12 +13621,17 @@
             interesting = true;
         }
 
-        if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ && app == mHeavyWeightProcess) {
-            // We don't want to kill the current heavy-weight process.
-            adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
-            schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.cached = false;
-            app.adjType = "heavy";
+        if (app == mHeavyWeightProcess) {
+            if (adj > ProcessList.HEAVY_WEIGHT_APP_ADJ) {
+                // We don't want to kill the current heavy-weight process.
+                adj = ProcessList.HEAVY_WEIGHT_APP_ADJ;
+                schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+                app.cached = false;
+                app.adjType = "heavy";
+            }
+            if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
+                procState = ActivityManager.PROCESS_STATE_HEAVY_WEIGHT;
+            }
         }
 
         if (app == mHomeProcess) {
@@ -13663,7 +13671,7 @@
         // this gives us a baseline and makes sure we don't get into an
         // infinite recursion.
         app.adjSeq = mAdjSeq;
-        app.curRawAdj = app.nonStoppingAdj = adj;
+        app.curRawAdj = adj;
         app.hasStartedServices = false;
 
         if (mBackupTarget != null && app == mBackupTarget.app) {
@@ -13682,243 +13690,220 @@
             }
         }
 
-        if (app.services.size() != 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) {
-            final long now = SystemClock.uptimeMillis();
-            // This process is more important if the top activity is
-            // bound to the service.
-            Iterator<ServiceRecord> jt = app.services.iterator();
-            while (jt.hasNext() && adj > ProcessList.FOREGROUND_APP_ADJ) {
-                ServiceRecord s = jt.next();
-                if (s.startRequested) {
-                    app.hasStartedServices = true;
-                    if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
-                        procState = ActivityManager.PROCESS_STATE_SERVICE;
-                    }
-                    if (app.hasShownUi && app != mHomeProcess) {
-                        // If this process has shown some UI, let it immediately
-                        // go to the LRU list because it may be pretty heavy with
-                        // UI stuff.  We'll tag it with a label just to help
-                        // debug and understand what is going on.
-                        if (adj > ProcessList.SERVICE_ADJ) {
-                            app.adjType = "cch-started-ui-services";
-                        }
-                    } else {
-                        if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) {
-                            // This service has seen some activity within
-                            // recent memory, so we will keep its process ahead
-                            // of the background processes.
-                            if (adj > ProcessList.SERVICE_ADJ) {
-                                adj = ProcessList.SERVICE_ADJ;
-                                app.adjType = "started-services";
-                                app.cached = false;
-                            }
-                        }
-                        // If we have let the service slide into the background
-                        // state, still have some text describing what it is doing
-                        // even though the service no longer has an impact.
-                        if (adj > ProcessList.SERVICE_ADJ) {
-                            app.adjType = "cch-started-services";
-                        }
-                    }
-                    // Don't kill this process because it is doing work; it
-                    // has said it is doing work.
-                    app.keeping = true;
+        for (int is = app.services.size()-1;
+                is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                        || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+                        || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+                is--) {
+            ServiceRecord s = app.services.valueAt(is);
+            if (s.startRequested) {
+                app.hasStartedServices = true;
+                if (procState > ActivityManager.PROCESS_STATE_SERVICE) {
+                    procState = ActivityManager.PROCESS_STATE_SERVICE;
                 }
-                for (int conni = s.connections.size()-1;
-                        conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                                || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
-                        conni--) {
-                    ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
-                    for (int i = 0;
-                            i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
-                                    || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
-                            i++) {
-                        // XXX should compute this based on the max of
-                        // all connected clients.
-                        ConnectionRecord cr = clist.get(i);
-                        if (cr.binding.client == app) {
-                            // Binding to ourself is not interesting.
-                            continue;
+                if (app.hasShownUi && app != mHomeProcess) {
+                    // If this process has shown some UI, let it immediately
+                    // go to the LRU list because it may be pretty heavy with
+                    // UI stuff.  We'll tag it with a label just to help
+                    // debug and understand what is going on.
+                    if (adj > ProcessList.SERVICE_ADJ) {
+                        app.adjType = "cch-started-ui-services";
+                    }
+                } else {
+                    if (now < (s.lastActivity + ActiveServices.MAX_SERVICE_INACTIVITY)) {
+                        // This service has seen some activity within
+                        // recent memory, so we will keep its process ahead
+                        // of the background processes.
+                        if (adj > ProcessList.SERVICE_ADJ) {
+                            adj = ProcessList.SERVICE_ADJ;
+                            app.adjType = "started-services";
+                            app.cached = false;
                         }
-                        if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
-                            ProcessRecord client = cr.binding.client;
-                            int myCachedAdj = cachedAdj;
-                            if (myCachedAdj > client.cachedAdj) {
-                                if (client.cachedAdj >= ProcessList.VISIBLE_APP_ADJ) {
-                                    myCachedAdj = client.cachedAdj;
-                                } else {
-                                    myCachedAdj = ProcessList.VISIBLE_APP_ADJ;
-                                }
-                            }
-                            int myClientCachedAdj = clientCachedAdj;
-                            if (myClientCachedAdj > client.clientCachedAdj) {
-                                if (client.clientCachedAdj >= ProcessList.VISIBLE_APP_ADJ) {
-                                    myClientCachedAdj = client.clientCachedAdj;
-                                } else {
-                                    myClientCachedAdj = ProcessList.VISIBLE_APP_ADJ;
-                                }
-                            }
-                            int myEmptyAdj = emptyAdj;
-                            if (myEmptyAdj > client.emptyAdj) {
-                                if (client.emptyAdj >= ProcessList.VISIBLE_APP_ADJ) {
-                                    myEmptyAdj = client.emptyAdj;
-                                } else {
-                                    myEmptyAdj = ProcessList.VISIBLE_APP_ADJ;
-                                }
-                            }
-                            int clientAdj = computeOomAdjLocked(client, myCachedAdj,
-                                    myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll);
-                            int clientProcState = client.curProcState;
-                            String adjType = null;
-                            if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
-                                // Not doing bind OOM management, so treat
-                                // this guy more like a started service.
-                                if (app.hasShownUi && app != mHomeProcess) {
-                                    // If this process has shown some UI, let it immediately
-                                    // go to the LRU list because it may be pretty heavy with
-                                    // UI stuff.  We'll tag it with a label just to help
-                                    // debug and understand what is going on.
-                                    if (adj > clientAdj) {
-                                        adjType = "cch-bound-ui-services";
-                                    }
-                                    app.cached = false;
-                                    clientAdj = adj;
-                                    clientProcState = procState;
-                                } else {
-                                    if (now >= (s.lastActivity
-                                            + ActiveServices.MAX_SERVICE_INACTIVITY)) {
-                                        // This service has not seen activity within
-                                        // recent memory, so allow it to drop to the
-                                        // LRU list if there is no other reason to keep
-                                        // it around.  We'll also tag it with a label just
-                                        // to help debug and undertand what is going on.
-                                        if (adj > clientAdj) {
-                                            adjType = "cch-bound-services";
-                                        }
-                                        clientAdj = adj;
-                                    }
-                                }
-                            } else if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
-                                if ((cr.flags&Context.BIND_NOT_VISIBLE) == 0) {
-                                    // If this connection is keeping the service
-                                    // created, then we want to try to better follow
-                                    // its memory management semantics for activities.
-                                    // That is, if it is sitting in the background
-                                    // LRU list as a cached process (with activities),
-                                    // we don't want the service it is connected to
-                                    // to go into the empty LRU and quickly get killed,
-                                    // because all we'll do is just end up restarting
-                                    // the service.
-                                    app.hasClientActivities |= client.hasActivities;
-                                }
-                            }
-                            if (adj > clientAdj) {
-                                // If this process has recently shown UI, and
-                                // the process that is binding to it is less
-                                // important than being visible, then we don't
-                                // care about the binding as much as we care
-                                // about letting this process get into the LRU
-                                // list to be killed and restarted if needed for
-                                // memory.
-                                if (app.hasShownUi && app != mHomeProcess
-                                        && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                    adjType = "cch-bound-ui-services";
-                                } else {
-                                    if ((cr.flags&(Context.BIND_ABOVE_CLIENT
-                                            |Context.BIND_IMPORTANT)) != 0) {
-                                        adj = clientAdj;
-                                    } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
-                                            && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
-                                            && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                                        adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                                    } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) {
-                                        adj = clientAdj;
-                                    } else {
-                                        app.pendingUiClean = true;
-                                        if (adj > ProcessList.VISIBLE_APP_ADJ) {
-                                            adj = ProcessList.VISIBLE_APP_ADJ;
-                                        }
-                                    }
-                                    if (!client.cached) {
-                                        app.cached = false;
-                                    }
-                                    if (client.keeping) {
-                                        app.keeping = true;
-                                    }
-                                    adjType = "service";
-                                }
-                            }
-                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
-                                if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
-                                    schedGroup = Process.THREAD_GROUP_DEFAULT;
-                                }
-                                if (clientProcState <
-                                        ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
-                                    clientProcState =
-                                            ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
-                                }
-                            } else {
-                                if (clientProcState <
-                                        ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
-                                    clientProcState =
-                                            ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
-                                }
-                            }
-                            if (procState > clientProcState) {
-                                procState = clientProcState;
-                            }
-                            if (adjType != null) {
-                                app.adjType = adjType;
-                                app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                        .REASON_SERVICE_IN_USE;
-                                app.adjSource = cr.binding.client;
-                                app.adjSourceOom = clientAdj;
-                                app.adjTarget = s.name;
-                            }
-                        }
-                        final ActivityRecord a = cr.activity;
-                        if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
-                            if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ &&
-                                    (a.visible || a.state == ActivityState.RESUMED
-                                     || a.state == ActivityState.PAUSING)) {
-                                adj = ProcessList.FOREGROUND_APP_ADJ;
-                                if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
-                                    schedGroup = Process.THREAD_GROUP_DEFAULT;
-                                }
-                                app.cached = false;
-                                app.adjType = "service";
-                                app.adjTypeCode = ActivityManager.RunningAppProcessInfo
-                                        .REASON_SERVICE_IN_USE;
-                                app.adjSource = a;
-                                app.adjSourceOom = adj;
-                                app.adjTarget = s.name;
-                            }
-                        }
+                    }
+                    // If we have let the service slide into the background
+                    // state, still have some text describing what it is doing
+                    // even though the service no longer has an impact.
+                    if (adj > ProcessList.SERVICE_ADJ) {
+                        app.adjType = "cch-started-services";
                     }
                 }
+                // Don't kill this process because it is doing work; it
+                // has said it is doing work.
+                app.keeping = true;
             }
-            
-            // Finally, if this process has active services running in it, we
-            // would like to avoid killing it unless it would prevent the current
-            // application from running.  By default we put the process in
-            // with the rest of the background processes; as we scan through
-            // its services we may bump it up from there.
-            if (adj > cachedAdj) {
-                adj = cachedAdj;
-                app.cached = false;
-                app.adjType = "cch-services";
+            for (int conni = s.connections.size()-1;
+                    conni >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
+                            || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+                            || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+                    conni--) {
+                ArrayList<ConnectionRecord> clist = s.connections.valueAt(conni);
+                for (int i = 0;
+                        i < clist.size() && (adj > ProcessList.FOREGROUND_APP_ADJ
+                                || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+                                || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+                        i++) {
+                    // XXX should compute this based on the max of
+                    // all connected clients.
+                    ConnectionRecord cr = clist.get(i);
+                    if (cr.binding.client == app) {
+                        // Binding to ourself is not interesting.
+                        continue;
+                    }
+                    if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
+                        ProcessRecord client = cr.binding.client;
+                        int clientAdj = computeOomAdjLocked(client, cachedAdj,
+                                TOP_APP, doingAll, now);
+                        int clientProcState = client.curProcState;
+                        String adjType = null;
+                        if ((cr.flags&Context.BIND_ALLOW_OOM_MANAGEMENT) != 0) {
+                            // Not doing bind OOM management, so treat
+                            // this guy more like a started service.
+                            if (app.hasShownUi && app != mHomeProcess) {
+                                // If this process has shown some UI, let it immediately
+                                // go to the LRU list because it may be pretty heavy with
+                                // UI stuff.  We'll tag it with a label just to help
+                                // debug and understand what is going on.
+                                if (adj > clientAdj) {
+                                    adjType = "cch-bound-ui-services";
+                                }
+                                app.cached = false;
+                                clientAdj = adj;
+                                clientProcState = procState;
+                            } else {
+                                if (now >= (s.lastActivity
+                                        + ActiveServices.MAX_SERVICE_INACTIVITY)) {
+                                    // This service has not seen activity within
+                                    // recent memory, so allow it to drop to the
+                                    // LRU list if there is no other reason to keep
+                                    // it around.  We'll also tag it with a label just
+                                    // to help debug and undertand what is going on.
+                                    if (adj > clientAdj) {
+                                        adjType = "cch-bound-services";
+                                    }
+                                    clientAdj = adj;
+                                }
+                            }
+                        } else if ((cr.flags&Context.BIND_AUTO_CREATE) != 0) {
+                            if ((cr.flags&Context.BIND_NOT_VISIBLE) == 0) {
+                                // If this connection is keeping the service
+                                // created, then we want to try to better follow
+                                // its memory management semantics for activities.
+                                // That is, if it is sitting in the background
+                                // LRU list as a cached process (with activities),
+                                // we don't want the service it is connected to
+                                // to go into the empty LRU and quickly get killed,
+                                // because all we'll do is just end up restarting
+                                // the service.
+                                if (client.hasActivities) {
+                                    if (procState >
+                                            ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT) {
+                                        procState =
+                                                ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT;
+                                        app.adjType = "cch-client-act";
+                                    }
+                                }
+                                app.hasClientActivities |= client.hasActivities;
+                            }
+                        }
+                        if (adj > clientAdj) {
+                            // If this process has recently shown UI, and
+                            // the process that is binding to it is less
+                            // important than being visible, then we don't
+                            // care about the binding as much as we care
+                            // about letting this process get into the LRU
+                            // list to be killed and restarted if needed for
+                            // memory.
+                            if (app.hasShownUi && app != mHomeProcess
+                                    && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                adjType = "cch-bound-ui-services";
+                            } else {
+                                if ((cr.flags&(Context.BIND_ABOVE_CLIENT
+                                        |Context.BIND_IMPORTANT)) != 0) {
+                                    adj = clientAdj;
+                                } else if ((cr.flags&Context.BIND_NOT_VISIBLE) != 0
+                                        && clientAdj < ProcessList.PERCEPTIBLE_APP_ADJ
+                                        && adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
+                                    adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+                                } else if (clientAdj > ProcessList.VISIBLE_APP_ADJ) {
+                                    adj = clientAdj;
+                                } else {
+                                    if (adj > ProcessList.VISIBLE_APP_ADJ) {
+                                        adj = ProcessList.VISIBLE_APP_ADJ;
+                                    }
+                                }
+                                if (!client.cached) {
+                                    app.cached = false;
+                                }
+                                if (client.keeping) {
+                                    app.keeping = true;
+                                }
+                                adjType = "service";
+                            }
+                        }
+                        if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+                            if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
+                                schedGroup = Process.THREAD_GROUP_DEFAULT;
+                            }
+                            if (clientProcState <
+                                    ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                                clientProcState =
+                                        ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                            }
+                        } else {
+                            if (clientProcState <
+                                    ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+                                clientProcState =
+                                        ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+                            }
+                        }
+                        if (procState > clientProcState) {
+                            procState = clientProcState;
+                        }
+                        if (procState < ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                                && (cr.flags&Context.BIND_SHOWING_UI) != 0) {
+                            app.pendingUiClean = true;
+                        }
+                        if (adjType != null) {
+                            app.adjType = adjType;
+                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                    .REASON_SERVICE_IN_USE;
+                            app.adjSource = cr.binding.client;
+                            app.adjSourceOom = clientAdj;
+                            app.adjTarget = s.name;
+                        }
+                    }
+                    final ActivityRecord a = cr.activity;
+                    if ((cr.flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
+                        if (a != null && adj > ProcessList.FOREGROUND_APP_ADJ &&
+                                (a.visible || a.state == ActivityState.RESUMED
+                                 || a.state == ActivityState.PAUSING)) {
+                            adj = ProcessList.FOREGROUND_APP_ADJ;
+                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
+                                schedGroup = Process.THREAD_GROUP_DEFAULT;
+                            }
+                            app.cached = false;
+                            app.adjType = "service";
+                            app.adjTypeCode = ActivityManager.RunningAppProcessInfo
+                                    .REASON_SERVICE_IN_USE;
+                            app.adjSource = a;
+                            app.adjSourceOom = adj;
+                            app.adjTarget = s.name;
+                        }
+                    }
+                }
             }
         }
 
         for (int provi = app.pubProviders.size()-1;
                 provi >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                        || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
+                        || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+                        || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
                 provi--) {
             ContentProviderRecord cpr = app.pubProviders.valueAt(provi);
             for (int i = cpr.connections.size()-1;
                     i >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ
-                            || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE);
+                            || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE
+                            || procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
                     i--) {
                 ContentProviderConnection conn = cpr.connections.get(i);
                 ProcessRecord client = conn.client;
@@ -13926,32 +13911,7 @@
                     // Being our own client is not interesting.
                     continue;
                 }
-                int myCachedAdj = cachedAdj;
-                if (myCachedAdj > client.cachedAdj) {
-                    if (client.cachedAdj > ProcessList.FOREGROUND_APP_ADJ) {
-                        myCachedAdj = client.cachedAdj;
-                    } else {
-                        myCachedAdj = ProcessList.FOREGROUND_APP_ADJ;
-                    }
-                }
-                int myClientCachedAdj = clientCachedAdj;
-                if (myClientCachedAdj > client.clientCachedAdj) {
-                    if (client.clientCachedAdj >= ProcessList.FOREGROUND_APP_ADJ) {
-                        myClientCachedAdj = client.clientCachedAdj;
-                    } else {
-                        myClientCachedAdj = ProcessList.FOREGROUND_APP_ADJ;
-                    }
-                }
-                int myEmptyAdj = emptyAdj;
-                if (myEmptyAdj > client.emptyAdj) {
-                    if (client.emptyAdj > ProcessList.FOREGROUND_APP_ADJ) {
-                        myEmptyAdj = client.emptyAdj;
-                    } else {
-                        myEmptyAdj = ProcessList.FOREGROUND_APP_ADJ;
-                    }
-                }
-                int clientAdj = computeOomAdjLocked(client, myCachedAdj,
-                        myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll);
+                int clientAdj = computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
                 if (adj > clientAdj) {
                     if (app.hasShownUi && app != mHomeProcess
                             && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -13961,12 +13921,8 @@
                                 ? clientAdj : ProcessList.FOREGROUND_APP_ADJ;
                         app.adjType = "provider";
                     }
-                    if (!client.cached) {
-                        app.cached = false;
-                    }
-                    if (client.keeping) {
-                        app.keeping = true;
-                    }
+                    app.cached &= client.cached;
+                    app.keeping |= client.keeping;
                     app.adjTypeCode = ActivityManager.RunningAppProcessInfo
                             .REASON_PROVIDER_IN_USE;
                     app.adjSource = client;
@@ -13974,7 +13930,10 @@
                     app.adjTarget = cpr.name;
                 }
                 if (procState > client.curProcState) {
-                    procState = client.curProcState;
+                    procState = client.curProcState >
+                            ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+                            ? client.curProcState
+                            : ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
                 }
                 if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
@@ -13992,8 +13951,8 @@
                     app.adjType = "provider";
                     app.adjTarget = cpr.name;
                 }
-                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE) {
-                    procState = ActivityManager.PROCESS_STATE_IMPORTANT_PERCEPTIBLE;
+                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                    procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
                 }
             }
         }
@@ -14010,16 +13969,6 @@
             app.serviceb = false;
         }
 
-        app.nonStoppingAdj = adj;
-
-        if (hasStoppingActivities) {
-            // Only upgrade adjustment.
-            if (adj > ProcessList.PERCEPTIBLE_APP_ADJ) {
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-                app.adjType = "stopping";
-            }
-        }
-
         app.curRawAdj = adj;
         
         //Slog.i(TAG, "OOM ADJ " + app + ": pid=" + app.pid +
@@ -14034,24 +13983,12 @@
             app.keeping = true;
         }
 
-        if (app.hasAboveClient) {
-            // If this process has bound to any services with BIND_ABOVE_CLIENT,
-            // then we need to drop its adjustment to be lower than the service's
-            // in order to honor the request.  We want to drop it by one adjustment
-            // level...  but there is special meaning applied to various levels so
-            // we will skip some of them.
-            if (adj < ProcessList.FOREGROUND_APP_ADJ) {
-                // System process will not get dropped, ever
-            } else if (adj < ProcessList.VISIBLE_APP_ADJ) {
-                adj = ProcessList.VISIBLE_APP_ADJ;
-            } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
-                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
-            } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
-                adj = ProcessList.CACHED_APP_MIN_ADJ;
-            } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
-                adj++;
-            }
-        }
+        // Do final modification to adj.  Everything we do between here and applying
+        // the final setAdj must be done in this function, because we will also use
+        // it when computing the final cached adj later.  Note that we don't need to
+        // worry about this for max adj above, since max adj will always be used to
+        // keep it out of the cached vaues.
+        adj = app.modifyRawOomAdj(adj);
 
         app.curProcState = procState;
 
@@ -14407,23 +14344,10 @@
         }
     }
 
-    private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
-            int clientCachedAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll,
-            boolean doingProcessState, long now) {
-        app.cachedAdj = cachedAdj;
-        app.clientCachedAdj = clientCachedAdj;
-        app.emptyAdj = emptyAdj;
-
-        if (app.thread == null) {
-            return false;
-        }
-
-        final boolean wasKeeping = app.keeping;
-
+    private final boolean applyOomAdjLocked(ProcessRecord app, boolean wasKeeping,
+            ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) {
         boolean success = true;
 
-        computeOomAdjLocked(app, cachedAdj, clientCachedAdj, emptyAdj, TOP_APP, false, doingAll);
-
         if (app.curRawAdj != app.setRawAdj) {
             if (wasKeeping && !app.keeping) {
                 // This app is no longer something we want to keep.  Note
@@ -14467,11 +14391,6 @@
                     requestPssLocked(app, now, true);
                 }
                 app.setAdj = app.curAdj;
-                app.setAdjChanged = true;
-                if (!doingAll) {
-                    app.setProcessTrackerState(TOP_APP, mProcessTracker.getMemFactorLocked(),
-                            now, mProcessList);
-                }
             } else {
                 success = false;
                 Slog.w(TAG, "Failed setting oom adj of " + app + " to " + app.curAdj);
@@ -14510,11 +14429,13 @@
                         }
                     }
                 }
+                Process.setSwappiness(app.pid,
+                        app.curSchedGroup <= Process.THREAD_GROUP_BG_NONINTERACTIVE);
             }
         }
         if (app.repProcState != app.curProcState) {
             app.repProcState = app.curProcState;
-            if (!doingProcessState && app.thread != null) {
+            if (!reportingProcessState && app.thread != null) {
                 try {
                     if (false) {
                         //RuntimeException h = new RuntimeException("here");
@@ -14526,9 +14447,33 @@
                 }
             }
         }
+        if (app.setProcState != app.curProcState) {
+            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                    "Proc state change of " + app.processName
+                    + " to " + app.curProcState);
+            app.setProcState = app.curProcState;
+            app.procStateChanged = true;
+            if (!doingAll) {
+                app.setProcessTrackerState(mProcessTracker.getMemFactorLocked(), now);
+            }
+        }
         return success;
     }
 
+    private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
+            ProcessRecord TOP_APP, boolean doingAll, boolean reportingProcessState, long now) {
+        if (app.thread == null) {
+            return false;
+        }
+
+        final boolean wasKeeping = app.keeping;
+
+        computeOomAdjLocked(app, cachedAdj, TOP_APP, doingAll, now);
+
+        return applyOomAdjLocked(app, wasKeeping, TOP_APP, doingAll,
+                reportingProcessState, now);
+    }
+
     private final ActivityRecord resumedAppLocked() {
         return mStackSupervisor.resumedAppLocked();
     }
@@ -14540,17 +14485,19 @@
     final boolean updateOomAdjLocked(ProcessRecord app, boolean doingProcessState) {
         final ActivityRecord TOP_ACT = resumedAppLocked();
         final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
-        int curAdj = app.curAdj;
-        final boolean wasCached = curAdj >= ProcessList.CACHED_APP_MIN_ADJ
-            && curAdj <= ProcessList.CACHED_APP_MAX_ADJ;
+        final boolean wasCached = app.cached;
 
         mAdjSeq++;
 
-        boolean success = updateOomAdjLocked(app, app.cachedAdj, app.clientCachedAdj,
-                app.emptyAdj, TOP_APP, false, doingProcessState, SystemClock.uptimeMillis());
-        final boolean nowCached = app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
-            && app.curAdj <= ProcessList.CACHED_APP_MAX_ADJ;
-        if (nowCached != wasCached) {
+        // This is the desired cached adjusment we want to tell it to use.
+        // If our app is currently cached, we know it, and that is it.  Otherwise,
+        // we don't know it yet, and it needs to now be cached we will then
+        // need to do a complete oom adj.
+        final int cachedAdj = app.curRawAdj >= ProcessList.CACHED_APP_MIN_ADJ
+                ? app.curRawAdj : ProcessList.UNKNOWN_ADJ;
+        boolean success = updateOomAdjLocked(app, cachedAdj, TOP_APP, false, doingProcessState,
+                SystemClock.uptimeMillis());
+        if (wasCached != app.cached || app.curRawAdj == ProcessList.UNKNOWN_ADJ) {
             // Changed to/from cached state, so apps after it in the LRU
             // list may also be changed.
             updateOomAdjLocked();
@@ -14563,6 +14510,7 @@
         final ProcessRecord TOP_APP = TOP_ACT != null ? TOP_ACT.app : null;
         final long now = SystemClock.uptimeMillis();
         final long oldTime = now - ProcessList.MAX_EMPTY_TIME;
+        final int N = mLruProcesses.size();
 
         if (false) {
             RuntimeException e = new RuntimeException();
@@ -14591,7 +14539,7 @@
         // them.
         int numSlots = (ProcessList.CACHED_APP_MAX_ADJ
                 - ProcessList.CACHED_APP_MIN_ADJ + 1) / 2;
-        int numEmptyProcs = mLruProcesses.size()- mNumNonCachedProcs - mNumCachedHiddenProcs;
+        int numEmptyProcs = N - mNumNonCachedProcs - mNumCachedHiddenProcs;
         if (numEmptyProcs > cachedProcessLimit) {
             // If there are more empty processes than our limit on cached
             // processes, then use the cached process limit for the factor.
@@ -14616,80 +14564,97 @@
 
         // First update the OOM adjustment for each of the
         // application processes based on their current state.
-        int i = mLruProcesses.size();
         int curCachedAdj = ProcessList.CACHED_APP_MIN_ADJ;
         int nextCachedAdj = curCachedAdj+1;
         int curEmptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
         int nextEmptyAdj = curEmptyAdj+2;
         int curClientCachedAdj = curEmptyAdj;
-        boolean changed = false;
-        while (i > 0) {
-            i--;
+        for (int i=0; i<N; i++) {
             ProcessRecord app = mLruProcesses.get(i);
-            //Slog.i(TAG, "OOM " + app + ": cur cached=" + curCachedAdj);
-            app.setAdjChanged = false;
-            updateOomAdjLocked(app, curCachedAdj, curClientCachedAdj, curEmptyAdj, TOP_APP,
-                    true, false, now);
-            changed |= app.setAdjChanged;
-            if (!app.killedBackground) {
-                if (app.curRawAdj == curCachedAdj && app.hasActivities) {
-                    // This process was assigned as a cached process...  step the
-                    // cached level.
-                    mNumCachedHiddenProcs++;
-                    if (curCachedAdj != nextCachedAdj) {
-                        stepCached++;
-                        if (stepCached >= cachedFactor) {
-                            stepCached = 0;
-                            curCachedAdj = nextCachedAdj;
-                            nextCachedAdj += 2;
-                            if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
-                            }
-                            if (curClientCachedAdj <= curCachedAdj) {
-                                curClientCachedAdj = curCachedAdj + 1;
-                                if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                    curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
+            if (!app.killedBackground && app.thread != null) {
+                app.procStateChanged = false;
+                final boolean wasKeeping = app.keeping;
+                computeOomAdjLocked(app, ProcessList.UNKNOWN_ADJ, TOP_APP, true, now);
+
+                // If we haven't yet assigned the final cached adj
+                // to the process, do that now.
+                if (app.curAdj >= ProcessList.UNKNOWN_ADJ) {
+                    switch (app.curProcState) {
+                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+                            // This process is a cached process holding activities...
+                            // assign it the next cached value for that type, and then
+                            // step that cached level.
+                            app.curRawAdj = curCachedAdj;
+                            app.curAdj = app.modifyRawOomAdj(curCachedAdj);
+                            if (curCachedAdj != nextCachedAdj) {
+                                stepCached++;
+                                if (stepCached >= cachedFactor) {
+                                    stepCached = 0;
+                                    curCachedAdj = nextCachedAdj;
+                                    nextCachedAdj += 2;
+                                    if (nextCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                        nextCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                                    }
+                                    if (curClientCachedAdj <= curCachedAdj) {
+                                        curClientCachedAdj = curCachedAdj + 1;
+                                        if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                            curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                                        }
+                                    }
                                 }
                             }
-                        }
-                    }
-                    numCached++;
-                    if (numCached > cachedProcessLimit) {
-                        Slog.i(TAG, "No longer want " + app.processName
-                                + " (pid " + app.pid + "): cached #" + numCached);
-                        EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
-                                app.processName, app.setAdj, "too many background");
-                        app.killedBackground = true;
-                        Process.killProcessQuiet(app.pid);
-                    }
-                } else if (app.curRawAdj == curCachedAdj && app.hasClientActivities) {
-                    // This process has a client that has activities.  We will have
-                    // given it the current cached adj; here we will just leave it
-                    // without stepping the cached adj.
-                    curClientCachedAdj++;
-                    if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                        curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
-                    }
-                } else {
-                    if (app.curRawAdj == curEmptyAdj || app.curRawAdj == curCachedAdj) {
-                        // This process was assigned as an empty process...  step the
-                        // empty level.
-                        if (curEmptyAdj != nextEmptyAdj) {
-                            stepEmpty++;
-                            if (stepEmpty >= emptyFactor) {
-                                stepEmpty = 0;
-                                curEmptyAdj = nextEmptyAdj;
-                                nextEmptyAdj += 2;
-                                if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
-                                    nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                            break;
+                        case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                            // Special case for cached client processes...  just step
+                            // down from after regular cached processes.
+                            app.curRawAdj = curClientCachedAdj;
+                            app.curAdj = app.modifyRawOomAdj(curClientCachedAdj);
+                            curClientCachedAdj++;
+                            if (curClientCachedAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                curClientCachedAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                            }
+                            break;
+                        default:
+                            // For everything else, assign next empty cached process
+                            // level and bump that up.  Note that this means that
+                            // long-running services that have dropped down to the
+                            // cached level will be treated as empty (since their process
+                            // state is still as a service), which is what we want.
+                            app.curRawAdj = curEmptyAdj;
+                            app.curAdj = app.modifyRawOomAdj(curEmptyAdj);
+                            if (curEmptyAdj != nextEmptyAdj) {
+                                stepEmpty++;
+                                if (stepEmpty >= emptyFactor) {
+                                    stepEmpty = 0;
+                                    curEmptyAdj = nextEmptyAdj;
+                                    nextEmptyAdj += 2;
+                                    if (nextEmptyAdj > ProcessList.CACHED_APP_MAX_ADJ) {
+                                        nextEmptyAdj = ProcessList.CACHED_APP_MAX_ADJ;
+                                    }
                                 }
                             }
-                        }
-                    } else if (app.curRawAdj < ProcessList.CACHED_APP_MIN_ADJ) {
-                        mNumNonCachedProcs++;
+                            break;
                     }
-                    if (app.curAdj >= ProcessList.CACHED_APP_MIN_ADJ
-                            && !app.hasClientActivities) {
+                }
+
+                applyOomAdjLocked(app, wasKeeping, TOP_APP, true, false, now);
+
+                // Count the number of process types.
+                switch (app.curProcState) {
+                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY:
+                    case ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT:
+                        mNumCachedHiddenProcs++;
+                        numCached++;
+                        if (numCached > cachedProcessLimit) {
+                            Slog.i(TAG, "No longer want " + app.processName
+                                    + " (pid " + app.pid + "): cached #" + numCached);
+                            EventLog.writeEvent(EventLogTags.AM_KILL, app.userId, app.pid,
+                                    app.processName, app.setAdj, "too many background");
+                            app.killedBackground = true;
+                            Process.killProcessQuiet(app.pid);
+                        }
+                        break;
+                    case ActivityManager.PROCESS_STATE_CACHED_EMPTY:
                         if (numEmpty > ProcessList.TRIM_EMPTY_APPS
                                 && app.lastActivityTime < oldTime) {
                             Slog.i(TAG, "No longer want " + app.processName
@@ -14711,8 +14676,12 @@
                                 Process.killProcessQuiet(app.pid);
                             }
                         }
-                    }
+                        break;
+                    default:
+                        mNumNonCachedProcs++;
+                        break;
                 }
+
                 if (app.isolated && app.services.size() <= 0) {
                     // If this is an isolated process, and there are no
                     // services running in it, then the process is no longer
@@ -14727,8 +14696,8 @@
                     app.killedBackground = true;
                     Process.killProcessQuiet(app.pid);
                 }
-                if (app.nonStoppingAdj >= ProcessList.HOME_APP_ADJ
-                        && app.nonStoppingAdj != ProcessList.SERVICE_B_ADJ
+
+                if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
                         && !app.killedBackground) {
                     numTrimming++;
                 }
@@ -14743,11 +14712,10 @@
         // are managing to keep around is less than half the maximum we desire;
         // if we are keeping a good number around, we'll let them use whatever
         // memory they want.
-        int memFactor = ProcessTracker.ADJ_MEM_FACTOR_NORMAL;
+        boolean allChanged;
         if (numCached <= ProcessList.TRIM_CACHED_APPS
                 && numEmpty <= ProcessList.TRIM_EMPTY_APPS) {
             final int numCachedAndEmpty = numCached + numEmpty;
-            final int N = mLruProcesses.size();
             int factor = numTrimming/3;
             int minFactor = 2;
             if (mHomeProcess != null) minFactor++;
@@ -14755,6 +14723,7 @@
             if (factor < minFactor) factor = minFactor;
             int step = 0;
             int fgTrimLevel;
+            int memFactor;
             if (numCachedAndEmpty <= ProcessList.TRIM_CRITICAL_THRESHOLD) {
                 fgTrimLevel = ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
                 memFactor = ProcessTracker.ADJ_MEM_FACTOR_CRITICAL;
@@ -14766,13 +14735,19 @@
                 memFactor = ProcessTracker.ADJ_MEM_FACTOR_MODERATE;
             }
             int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE;
-            for (i=0; i<N; i++) {
+            allChanged = mProcessTracker.setMemFactorLocked(memFactor, !mSleeping, now);
+            for (int i=N-1; i>=0; i--) {
                 ProcessRecord app = mLruProcesses.get(i);
-                if (app.nonStoppingAdj >= ProcessList.HOME_APP_ADJ
-                        && app.nonStoppingAdj != ProcessList.SERVICE_B_ADJ
+                if (allChanged || app.procStateChanged) {
+                    app.setProcessTrackerState(memFactor, now);
+                }
+                if (app.curProcState >= ActivityManager.PROCESS_STATE_HOME
                         && !app.killedBackground) {
                     if (app.trimMemoryLevel < curLevel && app.thread != null) {
                         try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                                    "Trimming memory of " + app.processName
+                                    + " to " + curLevel);
                             app.thread.scheduleTrimMemory(curLevel);
                         } catch (RemoteException e) {
                         }
@@ -14803,10 +14778,13 @@
                                 break;
                         }
                     }
-                } else if (app.nonStoppingAdj == ProcessList.HEAVY_WEIGHT_APP_ADJ) {
+                } else if (app.curProcState == ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
                     if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
                             && app.thread != null) {
                         try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                                    "Trimming memory of heavy-weight " + app.processName
+                                    + " to " + ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
                             app.thread.scheduleTrimMemory(
                                     ComponentCallbacks2.TRIM_MEMORY_BACKGROUND);
                         } catch (RemoteException e) {
@@ -14814,14 +14792,17 @@
                     }
                     app.trimMemoryLevel = ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
                 } else {
-                    if ((app.nonStoppingAdj > ProcessList.VISIBLE_APP_ADJ || app.systemNoUi)
-                            && app.pendingUiClean) {
+                    if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                            || app.systemNoUi) && app.pendingUiClean) {
                         // If this application is now in the background and it
                         // had done UI, then give it the special trim level to
                         // have it free UI resources.
                         final int level = ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN;
                         if (app.trimMemoryLevel < level && app.thread != null) {
                             try {
+                                if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                                        "Trimming memory of bg-ui " + app.processName
+                                        + " to " + level);
                                 app.thread.scheduleTrimMemory(level);
                             } catch (RemoteException e) {
                             }
@@ -14830,6 +14811,9 @@
                     }
                     if (app.trimMemoryLevel < fgTrimLevel && app.thread != null) {
                         try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                                    "Trimming memory of fg " + app.processName
+                                    + " to " + fgTrimLevel);
                             app.thread.scheduleTrimMemory(fgTrimLevel);
                         } catch (RemoteException e) {
                         }
@@ -14838,14 +14822,21 @@
                 }
             }
         } else {
-            final int N = mLruProcesses.size();
-            for (i=0; i<N; i++) {
+            allChanged = mProcessTracker.setMemFactorLocked(
+                    ProcessTracker.ADJ_MEM_FACTOR_NORMAL, !mSleeping, now);
+            for (int i=N-1; i>=0; i--) {
                 ProcessRecord app = mLruProcesses.get(i);
-                if ((app.nonStoppingAdj > ProcessList.VISIBLE_APP_ADJ || app.systemNoUi)
-                        && app.pendingUiClean) {
+                if (allChanged || app.procStateChanged) {
+                    app.setProcessTrackerState(ProcessTracker.ADJ_MEM_FACTOR_NORMAL, now);
+                }
+                if ((app.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+                        || app.systemNoUi) && app.pendingUiClean) {
                     if (app.trimMemoryLevel < ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
                             && app.thread != null) {
                         try {
+                            if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG,
+                                    "Trimming memory of ui hidden " + app.processName
+                                    + " to " + ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
                             app.thread.scheduleTrimMemory(
                                     ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
                         } catch (RemoteException e) {
@@ -14863,16 +14854,6 @@
             mStackSupervisor.scheduleDestroyAllActivities(null, "always-finish");
         }
 
-        boolean allChanged = mProcessTracker.setMemFactorLocked(memFactor, !mSleeping, now);
-        if (changed || allChanged) {
-            memFactor = mProcessTracker.getMemFactorLocked();
-            for (i=mLruProcesses.size()-1; i>=0; i--) {
-                ProcessRecord app = mLruProcesses.get(i);
-                if (allChanged || app.setAdjChanged) {
-                    app.setProcessTrackerState(TOP_APP, memFactor, now, mProcessList);
-                }
-            }
-        }
         if (allChanged) {
             requestPssAllProcsLocked(now, false);
         }
diff --git a/services/java/com/android/server/am/IntentBindRecord.java b/services/java/com/android/server/am/IntentBindRecord.java
index 6c84b9f..21cf266 100644
--- a/services/java/com/android/server/am/IntentBindRecord.java
+++ b/services/java/com/android/server/am/IntentBindRecord.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.Intent;
 import android.os.IBinder;
+import android.util.ArrayMap;
 
 import java.io.PrintWriter;
 import java.util.HashMap;
@@ -33,8 +34,8 @@
     /** The intent that is bound.*/
     final Intent.FilterComparison intent; // 
     /** All apps that have bound to this Intent. */
-    final HashMap<ProcessRecord, AppBindRecord> apps
-            = new HashMap<ProcessRecord, AppBindRecord>();
+    final ArrayMap<ProcessRecord, AppBindRecord> apps
+            = new ArrayMap<ProcessRecord, AppBindRecord>();
     /** Binder published from service. */
     IBinder binder;
     /** Set when we have initiated a request for this binder. */
@@ -62,15 +63,12 @@
                 pw.print(" received="); pw.print(received);
                 pw.print(" hasBound="); pw.print(hasBound);
                 pw.print(" doRebind="); pw.println(doRebind);
-        if (apps.size() > 0) {
-            Iterator<AppBindRecord> it = apps.values().iterator();
-            while (it.hasNext()) {
-                AppBindRecord a = it.next();
-                pw.print(prefix); pw.print("* Client AppBindRecord{");
-                        pw.print(Integer.toHexString(System.identityHashCode(a)));
-                        pw.print(' '); pw.print(a.client); pw.println('}');
-                a.dumpInIntentBind(pw, prefix + "  ");
-            }
+        for (int i=0; i<apps.size(); i++) {
+            AppBindRecord a = apps.valueAt(i);
+            pw.print(prefix); pw.print("* Client AppBindRecord{");
+                    pw.print(Integer.toHexString(System.identityHashCode(a)));
+                    pw.print(' '); pw.print(a.client); pw.println('}');
+            a.dumpInIntentBind(pw, prefix + "  ");
         }
     }
 
@@ -81,12 +79,11 @@
 
     int collectFlags() {
         int flags = 0;
-        if (apps.size() > 0) {
-            for (AppBindRecord app : apps.values()) {
-                if (app.connections.size() > 0) {
-                    for (ConnectionRecord conn : app.connections) {
-                        flags |= conn.flags;
-                    }
+        for (int i=apps.size()-1; i>=0; i--) {
+            AppBindRecord app = apps.valueAt(i);
+            if (app.connections.size() > 0) {
+                for (ConnectionRecord conn : app.connections) {
+                    flags |= conn.flags;
                 }
             }
         }
diff --git a/services/java/com/android/server/am/ProcessList.java b/services/java/com/android/server/am/ProcessList.java
index 6a72e44f..8b0466f 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -36,6 +36,11 @@
 
     // OOM adjustments for processes in various states:
 
+    // Adjustment used in certain places where we don't know it yet.
+    // (Generally this is something that is going to be cached, but we
+    // don't know the exact value in the cached range to assign yet.)
+    static final int UNKNOWN_ADJ = 16;
+
     // This is a process only hosting activities that are not visible,
     // so it can be killed without any disruption.
     static final int CACHED_APP_MAX_ADJ = 15;
@@ -62,14 +67,14 @@
     // have much of an impact as far as the user is concerned.
     static final int SERVICE_ADJ = 5;
 
-    // This is a process currently hosting a backup operation.  Killing it
-    // is not entirely fatal but is generally a bad idea.
-    static final int BACKUP_APP_ADJ = 4;
-
     // This is a process with a heavy-weight application.  It is in the
     // background, but we want to try to avoid killing it.  Value set in
     // system/rootdir/init.rc on startup.
-    static final int HEAVY_WEIGHT_APP_ADJ = 3;
+    static final int HEAVY_WEIGHT_APP_ADJ = 4;
+
+    // This is a process currently hosting a backup operation.  Killing it
+    // is not entirely fatal but is generally a bad idea.
+    static final int BACKUP_APP_ADJ = 3;
 
     // This is a process only hosting components that are perceptible to the
     // user, and we really want to avoid killing them, but they are not
@@ -163,34 +168,11 @@
 
     private boolean mHaveDisplaySize;
 
-    private final int[] mAdjToTrackedState = new int[CACHED_APP_MAX_ADJ+1];
-
     ProcessList() {
         MemInfoReader minfo = new MemInfoReader();
         minfo.readMemInfo();
         mTotalMemMb = minfo.getTotalSize()/(1024*1024);
         updateOomLevels(0, 0, false);
-        for (int i=0; i<=CACHED_APP_MAX_ADJ; i++) {
-            if (i <= FOREGROUND_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_FOREGROUND;
-            } else if (i <= VISIBLE_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_VISIBLE;
-            } else if (i <= PERCEPTIBLE_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_PERCEPTIBLE;
-            } else if (i <= BACKUP_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_BACKUP;
-            } else if (i <= SERVICE_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_SERVICE;
-            } else if (i <= HOME_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_HOME;
-            } else if (i <= PREVIOUS_APP_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_PREVIOUS;
-            } else if (i <= SERVICE_B_ADJ) {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_SERVICE;
-            } else {
-                mAdjToTrackedState[i] = ProcessTracker.STATE_CACHED;
-            }
-        }
     }
 
     void applyDisplaySize(WindowManagerService wm) {
@@ -256,11 +238,6 @@
         return mOomMinFree[mOomAdj.length-1] * 1024;
     }
 
-    int adjToTrackedState(int adj) {
-        return adj >= FOREGROUND_APP_ADJ
-                ? mAdjToTrackedState[adj] : ProcessTracker.STATE_PERSISTENT;
-    }
-
     private void writeFile(String path, String data) {
         FileOutputStream fos = null;
         try {
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index cb9a76d..5c1b7f24 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import android.util.ArraySet;
 import com.android.internal.os.BatteryStatsImpl;
 
 import android.app.ActivityManager;
@@ -64,12 +65,8 @@
     long lruWeight;             // Weight for ordering in LRU list
     long lastPssTime;           // Last time we requested PSS data
     int maxAdj;                 // Maximum OOM adjustment for this process
-    int cachedAdj;              // If cached, this is the adjustment to use
-    int clientCachedAdj;        // If empty but cached client, this is the adjustment to use
-    int emptyAdj;               // If empty, this is the adjustment to use
     int curRawAdj;              // Current OOM unlimited adjustment for this process
     int setRawAdj;              // Last set OOM unlimited adjustment for this process
-    int nonStoppingAdj;         // Adjustment not counting any stopping activities
     int curAdj;                 // Current OOM adjustment for this process
     int setAdj;                 // Last set OOM adjustment for this process
     int curSchedGroup;          // Currently desired scheduling class
@@ -78,6 +75,7 @@
     int memImportance;          // Importance constant computed from curAdj
     int curProcState = -1;      // Currently computed process state: ActivityManager.PROCESS_STATE_*
     int repProcState = -1;      // Last reported process state
+    int setProcState = -1;      // Last set process state in process tracker
     boolean serviceb;           // Process currently is on the service B list
     boolean keeping;            // Actively running code so don't kill due to that?
     boolean setIsForeground;    // Running foreground UI when last set?
@@ -92,7 +90,7 @@
     boolean hasAboveClient;     // Bound using BIND_ABOVE_CLIENT, so want to be lower
     boolean bad;                // True if disabled in the bad process list
     boolean killedBackground;   // True when proc has been killed due to too many bg
-    boolean setAdjChanged;      // Keep track of whether we changed 'setAdj'.
+    boolean procStateChanged;   // Keep track of whether we changed 'setAdj'.
     String waitingToKill;       // Process is waiting to be killed when in the bg; reason
     IBinder forcingToForeground;// Token that is forcing this process to be foreground
     int adjSeq;                 // Sequence id for identifying oom_adj assignment cycles
@@ -125,15 +123,15 @@
     // contains HistoryRecord objects
     final ArrayList<ActivityRecord> activities = new ArrayList<ActivityRecord>();
     // all ServiceRecord running in this process
-    final HashSet<ServiceRecord> services = new HashSet<ServiceRecord>();
+    final ArraySet<ServiceRecord> services = new ArraySet<ServiceRecord>();
     // services that are currently executing code (need to remain foreground).
-    final HashSet<ServiceRecord> executingServices
-             = new HashSet<ServiceRecord>();
+    final ArraySet<ServiceRecord> executingServices
+             = new ArraySet<ServiceRecord>();
     // All ConnectionRecord this process holds
-    final HashSet<ConnectionRecord> connections
-            = new HashSet<ConnectionRecord>();  
+    final ArraySet<ConnectionRecord> connections
+            = new ArraySet<ConnectionRecord>();
     // all IIntentReceivers that are registered from this process.
-    final HashSet<ReceiverList> receivers = new HashSet<ReceiverList>();
+    final ArraySet<ReceiverList> receivers = new ArraySet<ReceiverList>();
     // class (String) -> ContentProviderRecord
     final ArrayMap<String, ContentProviderRecord> pubProviders
             = new ArrayMap<String, ContentProviderRecord>();
@@ -215,12 +213,8 @@
                 pw.print(" cached="); pw.print(cached);
                 pw.print(" empty="); pw.println(empty);
         pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj);
-                pw.print(" cached="); pw.print(cachedAdj);
-                pw.print(" client="); pw.print(clientCachedAdj);
-                pw.print(" empty="); pw.print(emptyAdj);
                 pw.print(" curRaw="); pw.print(curRawAdj);
                 pw.print(" setRaw="); pw.print(setRawAdj);
-                pw.print(" nonStopping="); pw.print(nonStoppingAdj);
                 pw.print(" cur="); pw.print(curAdj);
                 pw.print(" set="); pw.println(setAdj);
         pw.print(prefix); pw.print("curSchedGroup="); pw.print(curSchedGroup);
@@ -228,7 +222,8 @@
                 pw.print(" systemNoUi="); pw.print(systemNoUi);
                 pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel);
         pw.print(prefix); pw.print("curProcState="); pw.print(curProcState);
-                pw.print(" repProcState="); pw.println(repProcState);
+                pw.print(" repProcState="); pw.print(repProcState);
+                pw.print(" setProcState="); pw.println(setProcState);
         pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq);
                 pw.print(" lruSeq="); pw.print(lruSeq);
                 pw.print(" lastPssTime="); pw.println(lastPssTime);
@@ -301,20 +296,20 @@
         }
         if (services.size() > 0) {
             pw.print(prefix); pw.println("Services:");
-            for (ServiceRecord sr : services) {
-                pw.print(prefix); pw.print("  - "); pw.println(sr);
+            for (int i=0; i<services.size(); i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(services.valueAt(i));
             }
         }
         if (executingServices.size() > 0) {
             pw.print(prefix); pw.println("Executing Services:");
-            for (ServiceRecord sr : executingServices) {
-                pw.print(prefix); pw.print("  - "); pw.println(sr);
+            for (int i=0; i<executingServices.size(); i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(executingServices.valueAt(i));
             }
         }
         if (connections.size() > 0) {
             pw.print(prefix); pw.println("Connections:");
-            for (ConnectionRecord cr : connections) {
-                pw.print(prefix); pw.print("  - "); pw.println(cr);
+            for (int i=0; i<connections.size(); i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(connections.valueAt(i));
             }
         }
         if (pubProviders.size() > 0) {
@@ -335,8 +330,8 @@
         }
         if (receivers.size() > 0) {
             pw.print(prefix); pw.println("Receivers:");
-            for (ReceiverList rl : receivers) {
-                pw.print(prefix); pw.print("  - "); pw.println(rl);
+            for (int i=0; i<receivers.size(); i++) {
+                pw.print(prefix); pw.print("  - "); pw.println(receivers.valueAt(i));
             }
         }
     }
@@ -353,8 +348,7 @@
         baseProcessTracker = tracker;
         pkgList.put(_info.packageName, tracker);
         thread = _thread;
-        maxAdj = ProcessList.CACHED_APP_MAX_ADJ;
-        cachedAdj = clientCachedAdj = emptyAdj = ProcessList.CACHED_APP_MIN_ADJ;
+        maxAdj = ProcessList.UNKNOWN_ADJ;
         curRawAdj = setRawAdj = -100;
         curAdj = setAdj = -100;
         persistent = false;
@@ -400,16 +394,37 @@
 
     void updateHasAboveClientLocked() {
         hasAboveClient = false;
-        if (connections.size() > 0) {
-            for (ConnectionRecord cr : connections) {
-                if ((cr.flags&Context.BIND_ABOVE_CLIENT) != 0) {
-                    hasAboveClient = true;
-                    break;
-                }
+        for (int i=connections.size()-1; i>=0; i--) {
+            ConnectionRecord cr = connections.valueAt(i);
+            if ((cr.flags&Context.BIND_ABOVE_CLIENT) != 0) {
+                hasAboveClient = true;
+                break;
             }
         }
     }
 
+    int modifyRawOomAdj(int adj) {
+        if (hasAboveClient) {
+            // If this process has bound to any services with BIND_ABOVE_CLIENT,
+            // then we need to drop its adjustment to be lower than the service's
+            // in order to honor the request.  We want to drop it by one adjustment
+            // level...  but there is special meaning applied to various levels so
+            // we will skip some of them.
+            if (adj < ProcessList.FOREGROUND_APP_ADJ) {
+                // System process will not get dropped, ever
+            } else if (adj < ProcessList.VISIBLE_APP_ADJ) {
+                adj = ProcessList.VISIBLE_APP_ADJ;
+            } else if (adj < ProcessList.PERCEPTIBLE_APP_ADJ) {
+                adj = ProcessList.PERCEPTIBLE_APP_ADJ;
+            } else if (adj < ProcessList.CACHED_APP_MIN_ADJ) {
+                adj = ProcessList.CACHED_APP_MIN_ADJ;
+            } else if (adj < ProcessList.CACHED_APP_MAX_ADJ) {
+                adj++;
+            }
+        }
+        return adj;
+    }
+
     public String toShortString() {
         if (shortStringName != null) {
             return shortStringName;
@@ -483,11 +498,8 @@
         }
     }
 
-    public void setProcessTrackerState(ProcessRecord TOP_APP, int memFactor, long now,
-            ProcessList plist) {
-        int state = this == TOP_APP ? ProcessTracker.STATE_TOP
-                : plist.adjToTrackedState(getSetAdjWithServices());
-        baseProcessTracker.setState(state, memFactor, now, pkgList);
+    public void setProcessTrackerState(int memFactor, long now) {
+        baseProcessTracker.setState(repProcState, memFactor, now, pkgList);
     }
 
     /*
diff --git a/services/java/com/android/server/am/ProcessTracker.java b/services/java/com/android/server/am/ProcessTracker.java
index 0ea93e8..ef032ba 100644
--- a/services/java/com/android/server/am/ProcessTracker.java
+++ b/services/java/com/android/server/am/ProcessTracker.java
@@ -52,26 +52,34 @@
     public static final int STATE_NOTHING = -1;
     public static final int STATE_PERSISTENT = 0;
     public static final int STATE_TOP = 1;
-    public static final int STATE_FOREGROUND = 2;
-    public static final int STATE_VISIBLE = 3;
-    public static final int STATE_PERCEPTIBLE = 4;
-    public static final int STATE_BACKUP = 5;
+    public static final int STATE_IMPORTANT_FOREGROUND = 2;
+    public static final int STATE_IMPORTANT_BACKGROUND = 3;
+    public static final int STATE_BACKUP = 4;
+    public static final int STATE_HEAVY_WEIGHT = 5;
     public static final int STATE_SERVICE = 6;
-    public static final int STATE_HOME = 7;
-    public static final int STATE_PREVIOUS = 8;
-    public static final int STATE_CACHED = 9;
-    public static final int STATE_COUNT = STATE_CACHED+1;
+    public static final int STATE_RECEIVER = 7;
+    public static final int STATE_HOME = 8;
+    public static final int STATE_LAST_ACTIVITY = 9;
+    public static final int STATE_CACHED_ACTIVITY = 10;
+    public static final int STATE_CACHED_ACTIVITY_CLIENT = 11;
+    public static final int STATE_CACHED_EMPTY = 12;
+    public static final int STATE_COUNT = STATE_CACHED_EMPTY+1;
 
-    static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, STATE_TOP,
-            STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE, STATE_BACKUP,
-            STATE_SERVICE, STATE_HOME, STATE_PREVIOUS, STATE_CACHED
+    static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT,
+            STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND,
+            STATE_BACKUP, STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_RECEIVER,
+            STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY,
+            STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY
     };
 
     public static final int PSS_SAMPLE_COUNT = 0;
     public static final int PSS_MINIMUM = 1;
     public static final int PSS_AVERAGE = 2;
     public static final int PSS_MAXIMUM = 3;
-    public static final int PSS_COUNT = PSS_MAXIMUM+1;
+    public static final int PSS_USS_MINIMUM = 4;
+    public static final int PSS_USS_AVERAGE = 5;
+    public static final int PSS_USS_MAXIMUM = 6;
+    public static final int PSS_COUNT = PSS_USS_MAXIMUM+1;
 
     public static final int ADJ_NOTHING = -1;
     public static final int ADJ_MEM_FACTOR_NORMAL = 0;
@@ -106,8 +114,9 @@
     static int OFFSET_INDEX_MASK = 0xffff;
 
     static final String[] STATE_NAMES = new String[] {
-            "Persistent ", "Top        ", "Foreground ", "Visible    ", "Perceptible",
-            "Backup     ", "Service    ", "Home       ", "Previous   ", "Cached     "
+            "Persistent", "Top       ", "Imp Fg    ", "Imp Bg    ",
+            "Backup    ", "Heavy Wght", "Service   ", "Receiver  ", "Home      ",
+            "Last Act  ", "Cch Actvty", "Cch Client", "Cch Empty "
     };
 
     static final String[] ADJ_SCREEN_NAMES_CSV = new String[] {
@@ -119,8 +128,9 @@
     };
 
     static final String[] STATE_NAMES_CSV = new String[] {
-            "pers", "top", "fore", "vis", "percept",
-            "backup", "service", "home", "prev", "cached"
+            "pers", "top", "impfg", "impbg", "backup", "heavy",
+            "service", "receiver", "home", "lastact",
+            "cch-activity", "cch-aclient", "cch-empty"
     };
 
     static final String[] ADJ_SCREEN_TAGS = new String[] {
@@ -132,8 +142,26 @@
     };
 
     static final String[] STATE_TAGS = new String[] {
-            "y", "t", "f", "v", "r",
-            "b", "s", "h", "p", "c"
+            "p", "t", "f", "b", "u", "w",
+            "s", "r", "h", "l", "a", "c", "e"
+    };
+
+    // Map from process states to the states we track.
+    static final int[] PROCESS_STATE_TO_STATE = new int[] {
+            STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT
+            STATE_PERSISTENT,               // ActivityManager.PROCESS_STATE_PERSISTENT_UI
+            STATE_TOP,                      // ActivityManager.PROCESS_STATE_TOP
+            STATE_IMPORTANT_FOREGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+            STATE_IMPORTANT_BACKGROUND,     // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+            STATE_BACKUP,                   // ActivityManager.PROCESS_STATE_BACKUP
+            STATE_HEAVY_WEIGHT,             // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+            STATE_SERVICE,                  // ActivityManager.PROCESS_STATE_SERVICE
+            STATE_RECEIVER,                 // ActivityManager.PROCESS_STATE_RECEIVER
+            STATE_HOME,                     // ActivityManager.PROCESS_STATE_HOME
+            STATE_LAST_ACTIVITY,            // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
+            STATE_CACHED_ACTIVITY,          // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
+            STATE_CACHED_ACTIVITY_CLIENT,   // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
+            STATE_CACHED_EMPTY,             // ActivityManager.PROCESS_STATE_CACHED_EMPTY
     };
 
     static final String CSV_SEP = "\t";
@@ -322,10 +350,20 @@
             return true;
         }
 
+        /**
+         * Update the current state of the given list of processes.
+         *
+         * @param state Current ActivityManager.PROCESS_STATE_*
+         * @param memFactor Current mem factor constant.
+         * @param now Current time.
+         * @param pkgList Processes to update.
+         */
         public void setState(int state, int memFactor, long now,
                 ArrayMap<String, ProcessTracker.ProcessState> pkgList) {
-            if (state != STATE_NOTHING) {
-                state += memFactor*STATE_COUNT;
+            if (state < 0) {
+                state = STATE_NOTHING;
+            } else {
+                state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT);
             }
 
             // First update the common process.
@@ -370,7 +408,7 @@
             mStartTime = now;
         }
 
-        public void addPss(long pss, boolean always) {
+        public void addPss(long pss, long uss, boolean always) {
             if (!always) {
                 if (mLastPssState == mCurState && SystemClock.uptimeMillis()
                         < (mLastPssTime+(30*1000))) {
@@ -399,16 +437,27 @@
                     longs[idx+PSS_MINIMUM] = pss;
                     longs[idx+PSS_AVERAGE] = pss;
                     longs[idx+PSS_MAXIMUM] = pss;
+                    longs[idx+PSS_USS_MINIMUM] = uss;
+                    longs[idx+PSS_USS_AVERAGE] = uss;
+                    longs[idx+PSS_USS_MAXIMUM] = uss;
                 } else {
                     longs[idx+PSS_SAMPLE_COUNT] = count+1;
                     if (longs[idx+PSS_MINIMUM] > pss) {
                         longs[idx+PSS_MINIMUM] = pss;
                     }
-                    longs[idx+PSS_AVERAGE] = (long)( ((longs[idx+PSS_AVERAGE]*(double)count)+pss)
-                            / (count+1) );
+                    longs[idx+PSS_AVERAGE] = (long)(
+                            ((longs[idx+PSS_AVERAGE]*(double)count)+pss) / (count+1) );
                     if (longs[idx+PSS_MAXIMUM] < pss) {
                         longs[idx+PSS_MAXIMUM] = pss;
                     }
+                    if (longs[idx+PSS_USS_MINIMUM] > uss) {
+                        longs[idx+PSS_USS_MINIMUM] = uss;
+                    }
+                    longs[idx+PSS_USS_AVERAGE] = (long)(
+                            ((longs[idx+PSS_USS_AVERAGE]*(double)count)+uss) / (count+1) );
+                    if (longs[idx+PSS_USS_MAXIMUM] < uss) {
+                        longs[idx+PSS_USS_MAXIMUM] = uss;
+                    }
                 }
             }
         }
@@ -480,6 +529,21 @@
             int idx = State.binarySearch(mPssTable, mPssTableSize, state);
             return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_MAXIMUM) : 0;
         }
+
+        long getPssUssMinimum(int state) {
+            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0;
+        }
+
+        long getPssUssAverage(int state) {
+            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0;
+        }
+
+        long getPssUssMaximum(int state) {
+            int idx = State.binarySearch(mPssTable, mPssTableSize, state);
+            return idx >= 0 ? mState.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0;
+        }
     }
 
     public static final class ServiceState {
@@ -589,10 +653,13 @@
 
     static final class State {
         // Current version of the parcel format.
-        private static final int PARCEL_VERSION = 3;
+        private static final int PARCEL_VERSION = 6;
         // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
         private static final int MAGIC = 0x50535453;
 
+        static final int FLAG_COMPLETE = 1<<0;
+        static final int FLAG_SHUTDOWN = 1<<1;
+
         final File mBaseDir;
         final ProcessTracker mProcessTracker;
         AtomicFile mFile;
@@ -603,6 +670,7 @@
         long mTimePeriodStartRealtime;
         long mTimePeriodEndRealtime;
         boolean mRunning;
+        int mFlags;
 
         final ProcessMap<PackageState> mPackages = new ProcessMap<PackageState>();
         final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>();
@@ -690,6 +758,7 @@
             Arrays.fill(mMemFactorDurations, 0);
             mMemFactor = STATE_NOTHING;
             mStartTime = 0;
+            mReadError = null;
         }
 
         private void buildTimePeriodStartClockStr() {
@@ -724,7 +793,7 @@
             }
         }
 
-        void readLocked() {
+        boolean readLocked() {
             try {
                 FileInputStream stream = mFile.openRead();
 
@@ -770,11 +839,14 @@
                             }
                         }
                     }
+                    return false;
                 }
             } catch (Throwable e) {
-                mReadError = "error reading: " + e;
+                mReadError = "caught exception: " + e;
                 Slog.e(TAG, "Error reading process statistics", e);
+                return false;
             }
+            return true;
         }
 
         private void writeStateLocked(boolean sync, final boolean commit) {
@@ -848,6 +920,7 @@
             out.writeLong(mTimePeriodStartClock);
             out.writeLong(mTimePeriodStartRealtime);
             out.writeLong(mTimePeriodEndRealtime);
+            out.writeInt(mFlags);
 
             out.writeInt(mLongs.size());
             out.writeInt(mNextLong);
@@ -920,7 +993,7 @@
         private boolean readCheckedInt(Parcel in, int val, String what) {
             int got;
             if ((got=in.readInt()) != val) {
-                mReadError = "bad " + ": " + got;
+                mReadError = "bad " + what + ": " + got;
                 return false;
             }
             return true;
@@ -956,6 +1029,7 @@
             buildTimePeriodStartClockStr();
             mTimePeriodStartRealtime = in.readLong();
             mTimePeriodEndRealtime = in.readLong();
+            mFlags = in.readInt();
 
             final int NLONGS = in.readInt();
             final int NEXTLONG = in.readInt();
@@ -1365,8 +1439,9 @@
 
         void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now) {
             dumpFilteredSummaryLocked(pw, null, "  ", ALL_SCREEN_ADJ, ALL_MEM_ADJ,
-                    new int[] { STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
-                            STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME, STATE_PREVIOUS },
+                    new int[] { STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND,
+                            STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT,
+                            STATE_SERVICE, STATE_RECEIVER, STATE_HOME, STATE_LAST_ACTIVITY },
                     now, reqPackage);
             pw.println();
             pw.println("Run time Stats:");
@@ -1379,6 +1454,9 @@
             TimeUtils.formatDuration(
                     (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime)
                             - mTimePeriodStartRealtime, pw);
+            if ((mFlags&FLAG_COMPLETE) != 0) pw.print(" (complete)");
+            else if ((mFlags&FLAG_SHUTDOWN) != 0) pw.print(" (shutdown)");
+            else pw.print(" (partial)");
             pw.println();
         }
 
@@ -1452,10 +1530,13 @@
         void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
             final long now = SystemClock.uptimeMillis();
             ArrayMap<String, SparseArray<PackageState>> pkgMap = mPackages.getMap();
-            pw.println("vers,1");
+            pw.println("vers,2");
             pw.print("period,"); pw.print(mTimePeriodStartClockStr);
             pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
             pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
+            if ((mFlags&FLAG_COMPLETE) != 0) pw.print(",complete");
+            else if ((mFlags&FLAG_SHUTDOWN) != 0) pw.print(",shutdown");
+            else pw.print(",partial");
             pw.println();
             for (int ip=0; ip<pkgMap.size(); ip++) {
                 String pkgName = pkgMap.keyAt(ip);
@@ -1629,6 +1710,7 @@
         if (now > (mState.mLastWriteTime+WRITE_PERIOD)) {
             if (SystemClock.elapsedRealtime() > (mState.mTimePeriodStartRealtime+COMMIT_PERIOD)) {
                 mCommitPending = true;
+                mState.mFlags |= State.FLAG_COMPLETE;
             }
             return true;
         }
@@ -1637,6 +1719,7 @@
 
     public void shutdownLocked() {
         Slog.w(TAG, "Writing process stats before shutdown...");
+        mState.mFlags |= State.FLAG_SHUTDOWN;
         writeStateSyncLocked();
         mShuttingDown = true;
     }
@@ -1841,6 +1924,9 @@
         long minPss;
         long avgPss;
         long maxPss;
+        long minUss;
+        long avgUss;
+        long maxUss;
 
         ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) {
             screenStates = _screenStates;
@@ -1857,6 +1943,12 @@
                 printSizeValue(pw, avgPss * 1024);
                 pw.print("-");
                 printSizeValue(pw, maxPss * 1024);
+                pw.print("/");
+                printSizeValue(pw, minUss * 1024);
+                pw.print("-");
+                printSizeValue(pw, avgUss * 1024);
+                pw.print("-");
+                printSizeValue(pw, maxUss * 1024);
                 if (full) {
                     pw.print(" over ");
                     pw.print(numPss);
@@ -1868,7 +1960,8 @@
 
     static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) {
         data.totalTime = 0;
-        data.numPss = data.minPss = data.avgPss = data.maxPss = 0;
+        data.numPss = data.minPss = data.avgPss = data.maxPss =
+                data.minUss = data.avgUss = data.maxUss = 0;
         for (int is=0; is<data.screenStates.length; is++) {
             for (int im=0; im<data.memStates.length; im++) {
                 for (int ip=0; ip<data.procStates.length; ip++) {
@@ -1880,10 +1973,16 @@
                         long minPss = proc.getPssMinimum(bucket);
                         long avgPss = proc.getPssAverage(bucket);
                         long maxPss = proc.getPssMaximum(bucket);
+                        long minUss = proc.getPssUssMinimum(bucket);
+                        long avgUss = proc.getPssUssAverage(bucket);
+                        long maxUss = proc.getPssUssMaximum(bucket);
                         if (data.numPss == 0) {
                             data.minPss = minPss;
                             data.avgPss = avgPss;
                             data.maxPss = maxPss;
+                            data.minUss = minUss;
+                            data.avgUss = avgUss;
+                            data.maxUss = maxUss;
                         } else {
                             if (minPss < data.minPss) {
                                 data.minPss = minPss;
@@ -1893,6 +1992,14 @@
                             if (maxPss > data.maxPss) {
                                 data.maxPss = maxPss;
                             }
+                            if (minUss < data.minUss) {
+                                data.minUss = minUss;
+                            }
+                            data.avgUss = (long)( ((data.avgUss*(double)data.numPss)
+                                    + (avgUss*(double)samples)) / (data.numPss+samples) );
+                            if (maxUss > data.maxUss) {
+                                data.maxUss = maxUss;
+                            }
                         }
                         data.numPss += samples;
                     }
@@ -1989,7 +2096,7 @@
                     if (count > 0) {
                         if (!printedHeader) {
                             pw.print(prefix);
-                            pw.print("PSS (");
+                            pw.print("PSS/USS (");
                             pw.print(proc.mPssTableSize);
                             pw.println(" entries):");
                             printedHeader = true;
@@ -2013,6 +2120,12 @@
                         printSizeValue(pw, proc.getPssAverage(bucket) * 1024);
                         pw.print(" ");
                         printSizeValue(pw, proc.getPssMaximum(bucket) * 1024);
+                        pw.print(" / ");
+                        printSizeValue(pw, proc.getPssUssMinimum(bucket) * 1024);
+                        pw.print(" ");
+                        printSizeValue(pw, proc.getPssUssAverage(bucket) * 1024);
+                        pw.print(" ");
+                        printSizeValue(pw, proc.getPssUssMaximum(bucket) * 1024);
                         pw.println();
                     }
                 }
@@ -2148,21 +2261,28 @@
             dumpProcessSummaryDetails(pw, proc, prefix, "         TOTAL: ", screenStates, memStates,
                     procStates, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "    Persistent: ", screenStates, memStates,
-                    new int[] {STATE_PERSISTENT}, now, true);
+                    new int[] { STATE_PERSISTENT }, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "           Top: ", screenStates, memStates,
                     new int[] {STATE_TOP}, now, true);
-            dumpProcessSummaryDetails(pw, proc, prefix, "    Foreground: ", screenStates, memStates,
-                    new int[] {STATE_FOREGROUND, STATE_VISIBLE, STATE_PERCEPTIBLE}, now, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "        Imp Fg: ", screenStates, memStates,
+                    new int[] { STATE_IMPORTANT_FOREGROUND }, now, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "        Imp Bg: ", screenStates, memStates,
+                    new int[] {STATE_IMPORTANT_BACKGROUND}, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "        Backup: ", screenStates, memStates,
                     new int[] {STATE_BACKUP}, now, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "     Heavy Wgt: ", screenStates, memStates,
+                    new int[] {STATE_HEAVY_WEIGHT}, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "       Service: ", screenStates, memStates,
                     new int[] {STATE_SERVICE}, now, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "      Receiver: ", screenStates, memStates,
+                    new int[] {STATE_RECEIVER}, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "          Home: ", screenStates, memStates,
                     new int[] {STATE_HOME}, now, true);
-            dumpProcessSummaryDetails(pw, proc, prefix, "      Previous: ", screenStates, memStates,
-                    new int[] {STATE_PREVIOUS}, now, true);
+            dumpProcessSummaryDetails(pw, proc, prefix, "      Last Act: ", screenStates, memStates,
+                    new int[] {STATE_LAST_ACTIVITY}, now, true);
             dumpProcessSummaryDetails(pw, proc, prefix, "      (Cached): ", screenStates, memStates,
-                    new int[] {STATE_CACHED}, now, true);
+                    new int[] {STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_ACTIVITY_CLIENT,
+                            STATE_CACHED_EMPTY}, now, true);
         }
     }
 
@@ -2193,9 +2313,9 @@
         if (result < 1) {
             value = String.format("%.2f", result);
         } else if (result < 10) {
-            value = String.format("%.2f", result);
+            value = String.format("%.1f", result);
         } else if (result < 100) {
-            value = String.format("%.2f", result);
+            value = String.format("%.0f", result);
         } else {
             value = String.format("%.0f", result);
         }
@@ -2300,6 +2420,9 @@
             long min = proc.mState.getLong(off, PSS_MINIMUM);
             long avg = proc.mState.getLong(off, PSS_AVERAGE);
             long max = proc.mState.getLong(off, PSS_MAXIMUM);
+            long umin = proc.mState.getLong(off, PSS_USS_MINIMUM);
+            long uavg = proc.mState.getLong(off, PSS_USS_AVERAGE);
+            long umax = proc.mState.getLong(off, PSS_USS_MAXIMUM);
             pw.print(',');
             printProcStateTag(pw, type);
             pw.print(':');
@@ -2310,6 +2433,12 @@
             pw.print(avg);
             pw.print(':');
             pw.print(max);
+            pw.print(':');
+            pw.print(umin);
+            pw.print(':');
+            pw.print(uavg);
+            pw.print(':');
+            pw.print(umax);
         }
     }
 
@@ -2391,9 +2520,10 @@
         int[] csvMemStats = new int[] {ADJ_MEM_FACTOR_CRITICAL};
         boolean csvSepProcStats = true;
         int[] csvProcStats = new int[] {
-                STATE_PERSISTENT, STATE_TOP, STATE_FOREGROUND, STATE_VISIBLE,
-                STATE_PERCEPTIBLE, STATE_BACKUP, STATE_SERVICE, STATE_HOME,
-                STATE_PREVIOUS, STATE_CACHED };
+                STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND,
+                STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT, STATE_SERVICE,
+                STATE_RECEIVER, STATE_HOME, STATE_LAST_ACTIVITY,
+                STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY };
         if (args != null) {
             for (int i=0; i<args.length; i++) {
                 String arg = args[i];
@@ -2457,6 +2587,7 @@
                 } else if ("--current".equals(arg)) {
                     currentOnly = true;
                 } else if ("--commit".equals(arg)) {
+                    mState.mFlags |= State.FLAG_COMPLETE;
                     mState.writeStateLocked(true, true);
                     pw.println("Process stats committed.");
                     return;
@@ -2557,6 +2688,13 @@
                         if (DEBUG) Slog.d(TAG, "Retrieving state: " + files.get(i));
                         try {
                             State state = new State(files.get(i));
+                            if (state.mReadError != null) {
+                                pw.print("Failure reading "); pw.print(files.get(i));
+                                pw.print("; "); pw.println(state.mReadError);
+                                if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
+                                (new File(files.get(i))).delete();
+                                continue;
+                            }
                             String fileStr = state.mFile.getBaseFile().getPath();
                             boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
                             if (isCheckin || isCompact) {
@@ -2588,7 +2726,6 @@
                             pw.print("**** FAILURE DUMPING STATE: "); pw.println(files.get(i));
                             e.printStackTrace(pw);
                         }
-                        if (DEBUG) Slog.d(TAG, "Deleting state: " + files.get(i));
                     }
                 }
             } finally {
diff --git a/services/java/com/android/server/content/SyncManager.java b/services/java/com/android/server/content/SyncManager.java
index 9e2e841..2da95c3 100644
--- a/services/java/com/android/server/content/SyncManager.java
+++ b/services/java/com/android/server/content/SyncManager.java
@@ -1273,7 +1273,7 @@
                 for (int i = 0; i < settings.periodicSyncs.size(); i++) {
                     final Pair<Bundle, Long> pair = settings.periodicSyncs.get(i);
                     final String period = String.valueOf(pair.second);
-                    final String extras = pair.first.size() > 0 ? pair.first.toString() : "";
+                    final String extras = pair.first.size() > 0 ? " " + pair.first.toString() : "";
                     final String next = formatTime(status.getPeriodicSyncTime(i)
                             + pair.second * 1000);
                     table.set(row + i * 2, 12, period + extras);
diff --git a/services/java/com/android/server/dreams/DreamManagerService.java b/services/java/com/android/server/dreams/DreamManagerService.java
index c9e0da5..21e54fe 100644
--- a/services/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/java/com/android/server/dreams/DreamManagerService.java
@@ -73,7 +73,7 @@
         mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
     }
 
-    public void systemReady() {
+    public void systemRunning() {
         mContext.registerReceiver(new BroadcastReceiver() {
             @Override
             public void onReceive(Context context, Intent intent) {
diff --git a/services/java/com/android/server/input/InputManagerService.java b/services/java/com/android/server/input/InputManagerService.java
index 5e4907e..7b4c077 100644
--- a/services/java/com/android/server/input/InputManagerService.java
+++ b/services/java/com/android/server/input/InputManagerService.java
@@ -283,7 +283,7 @@
     }
 
     // TODO(BT) Pass in paramter for bluetooth system
-    public void systemReady() {
+    public void systemRunning() {
         if (DEBUG) {
             Slog.d(TAG, "System ready.");
         }
diff --git a/services/java/com/android/server/print/PrintManagerService.java b/services/java/com/android/server/print/PrintManagerService.java
index 5173998..86e7685 100644
--- a/services/java/com/android/server/print/PrintManagerService.java
+++ b/services/java/com/android/server/print/PrintManagerService.java
@@ -22,118 +22,111 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
-import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
 import android.os.Process;
-import android.os.RemoteException;
 import android.os.UserHandle;
-import android.print.IPrintAdapter;
 import android.print.IPrintClient;
+import android.print.IPrintDocumentAdapter;
 import android.print.IPrintManager;
-import android.print.IPrinterDiscoveryObserver;
 import android.print.PrintAttributes;
 import android.print.PrintJobInfo;
-import android.print.PrintManager;
-import android.print.PrinterId;
-import android.print.PrinterInfo;
-import android.printservice.IPrintService;
-import android.printservice.IPrintServiceClient;
-import android.printservice.PrintServiceInfo;
 import android.provider.Settings;
-import android.text.TextUtils;
-import android.text.TextUtils.SimpleStringSplitter;
-import android.util.Slog;
+import android.util.SparseArray;
 
 import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Map;
-import java.util.NoSuchElementException;
 import java.util.Set;
 
 public final class PrintManagerService extends IPrintManager.Stub {
 
-    private static final String LOG_TAG = PrintManagerService.class.getSimpleName();
-
     private static final char COMPONENT_NAME_SEPARATOR = ':';
 
     private final Object mLock = new Object();
 
-    private final SimpleStringSplitter mStringColonSplitter =
-            new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
-
-    private final Map<ComponentName, PrintServiceClient> mServices =
-            new HashMap<ComponentName, PrintServiceClient>();
-
-    private final List<PrintServiceInfo> mInstalledServices = new ArrayList<PrintServiceInfo>();
-
-    private final Set<ComponentName> mEnabledServiceNames = new HashSet<ComponentName>();
-
     private final Context mContext;
 
-    private final RemoteSpooler mSpooler;
-
-    private final int mMyUid;
+    private final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
 
     private int mCurrentUserId = UserHandle.USER_OWNER;
 
-    private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
-
     public PrintManagerService(Context context) {
         mContext = context;
-        mSpooler = new RemoteSpooler(context);
-        mMyUid = android.os.Process.myUid();
         registerContentObservers();
-        registerBoradcastreceivers();
+        registerBoradcastReceivers();
+    }
+
+    public void systemRuning() {
+        BackgroundThread.getHandler().post(new Runnable() {
+            @Override
+            public void run() {
+                synchronized (mLock) {
+                    UserState userState = getCurrentUserStateLocked();
+                    userState.updateIfNeededLocked();
+                    userState.getSpoolerLocked().notifyClientForActivteJobs();
+                }
+            }
+        });
     }
 
     @Override
-    public PrintJobInfo print(String printJobName, IPrintClient client, IPrintAdapter printAdapter,
-            PrintAttributes attributes, int appId, int userId) {
-        final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
-        final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+    public PrintJobInfo print(String printJobName, IPrintClient client,
+            IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId,
+            int userId) {
+        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+        final UserState userState;
+        final RemotePrintSpooler spooler;
+        synchronized (mLock) {
+            userState = getOrCreateUserStateLocked(resolvedUserId);
+            spooler = userState.getSpoolerLocked();
+        }
         final long identity = Binder.clearCallingIdentity();
         try {
-            return mSpooler.createPrintJob(printJobName, client, printAdapter,
-                    attributes, resolvedAppId, resolvedUserId);
+            return spooler.createPrintJob(printJobName, client, documentAdapter,
+                    attributes, resolvedAppId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
     @Override
-    public List<PrintJobInfo> getPrintJobs(int appId, int userId) {
-        final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
-        final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
-        // TODO: Do we want to return jobs in STATE_CREATED? We should probably
-        // have additional argument for the types to get
+    public List<PrintJobInfo> getPrintJobInfos(int appId, int userId) {
+        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+        final UserState userState;
+        final RemotePrintSpooler spooler;
+        synchronized (mLock) {
+            userState = getOrCreateUserStateLocked(resolvedUserId);
+            spooler = userState.getSpoolerLocked();
+        }
         final long identity = Binder.clearCallingIdentity();
         try {
-            return mSpooler.getPrintJobs(null, PrintJobInfo.STATE_ANY,
-                    resolvedAppId, resolvedUserId);
+            return spooler.getPrintJobInfos(null, PrintJobInfo.STATE_ANY,
+                    resolvedAppId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
     }
 
     @Override
-    public PrintJobInfo getPrintJob(int printJobId, int appId, int userId) {
-        final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
-        final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+    public PrintJobInfo getPrintJobInfo(int printJobId, int appId, int userId) {
+        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+        final UserState userState;
+        final RemotePrintSpooler spooler;
+        synchronized (mLock) {
+            userState = getOrCreateUserStateLocked(resolvedUserId);
+            spooler = userState.getSpoolerLocked();
+        }
         final long identity = Binder.clearCallingIdentity();
         try {
-            return mSpooler.getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
+            return spooler.getPrintJobInfo(printJobId, resolvedAppId);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -141,93 +134,32 @@
 
     @Override
     public void cancelPrintJob(int printJobId, int appId, int userId) {
-        final int resolvedAppId = resolveCallingAppEnforcingPermissionsLocked(appId);
-        final int resolvedUserId = resolveCallingUserEnforcingPermissionsIdLocked(userId);
+        final int resolvedAppId = resolveCallingAppEnforcingPermissions(appId);
+        final int resolvedUserId = resolveCallingUserEnforcingPermissions(userId);
+        final UserState userState;
+        final RemotePrintSpooler spooler;
+        synchronized (mLock) {
+            userState = getOrCreateUserStateLocked(resolvedUserId);
+            spooler = userState.getSpoolerLocked();
+        }
         final long identity = Binder.clearCallingIdentity();
         try {
-            if (mSpooler.cancelPrintJob(printJobId, resolvedAppId, resolvedUserId)) {
+            if (spooler.cancelPrintJob(printJobId, resolvedAppId)) {
                 return;
             }
-            PrintJobInfo printJob = getPrintJob(printJobId, resolvedAppId, resolvedUserId);
-            if (printJob == null) {
+            PrintJobInfo printJobInfo = getPrintJobInfo(printJobId, resolvedAppId, resolvedUserId);
+            if (printJobInfo == null) {
                 return;
             }
-            ComponentName printServiceName = printJob.getPrinterId().getServiceComponentName();
-            PrintServiceClient printService = null;
+            ComponentName printServiceName = printJobInfo.getPrinterId().getService();
+            RemotePrintService printService = null;
             synchronized (mLock) {
-                printService = mServices.get(printServiceName);
+                printService = userState.getActiveServices().get(printServiceName);
             }
             if (printService == null) {
                 return;
             }
-            printService.requestCancelPrintJob(printJob);
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    // Called only from the spooler.
-    @Override
-    public void onPrintJobQueued(PrinterId printerId, PrintJobInfo printJob) {
-        throwIfCallerNotSignedWithSystemKey();
-        PrintServiceClient printService = null;
-        synchronized (mLock) {
-            ComponentName printServiceName = printerId.getServiceComponentName();
-            printService = mServices.get(printServiceName);
-        } 
-        if (printService != null) {
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                printService.notifyPrintJobQueued(printJob);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-        }
-    }
-
-    // Called only from the spooler.
-    @Override
-    public void startDiscoverPrinters(IPrinterDiscoveryObserver observer) {
-        throwIfCallerNotSignedWithSystemKey();
-        List<PrintServiceClient> services = new ArrayList<PrintServiceClient>();
-        synchronized (mLock) {
-            mPrinterDiscoveryObserver = observer;
-            services.addAll(mServices.values());
-        }
-        final int serviceCount = services.size();
-        if (serviceCount <= 0) {
-            return;
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            for (int i = 0; i < serviceCount; i++) {
-                PrintServiceClient service = services.get(i);
-                service.startPrinterDiscovery();
-            }
-        } finally {
-            Binder.restoreCallingIdentity(identity);
-        }
-    }
-
-    // Called only from the spooler.
-    @Override
-    public void stopDiscoverPrinters() {
-        throwIfCallerNotSignedWithSystemKey();
-        List<PrintServiceClient> services = new ArrayList<PrintServiceClient>();
-        synchronized (mLock) {
-            mPrinterDiscoveryObserver = null;
-            services.addAll(mServices.values());
-        }
-        final int serviceCount = services.size();
-        if (serviceCount <= 0) {
-            return;
-        }
-        final long identity = Binder.clearCallingIdentity();
-        try {
-            for (int i = 0; i < serviceCount; i++) {
-                PrintServiceClient service = services.get(i);
-                service.stopPrintersDiscovery();
-            }
+            printService.onRequestCancelPrintJob(printJobInfo);
         } finally {
             Binder.restoreCallingIdentity(identity);
         }
@@ -237,14 +169,13 @@
         final Uri enabledPrintServicesUri = Settings.Secure.getUriFor(
                 Settings.Secure.ENABLED_PRINT_SERVICES);
 
-        ContentObserver observer = new ContentObserver(new Handler(mContext.getMainLooper())) {
+        ContentObserver observer = new ContentObserver(BackgroundThread.getHandler()) {
             @Override
             public void onChange(boolean selfChange, Uri uri) {
                 if (enabledPrintServicesUri.equals(uri)) {
                     synchronized (mLock) {
-                        if (readEnabledPrintServicesChangedLocked()) {
-                            onUserStateChangedLocked();
-                        }
+                        UserState userState = getCurrentUserStateLocked();
+                        userState.updateIfNeededLocked();
                     }
                 }
             }
@@ -254,32 +185,37 @@
                 false, observer, UserHandle.USER_ALL);
     }
 
-    private void registerBoradcastreceivers() {
+    private void registerBoradcastReceivers() {
         PackageMonitor monitor = new PackageMonitor() {
             @Override
-            public void onSomePackagesChanged() {
+            public boolean onPackageChanged(String packageName, int uid, String[] components) {
                 synchronized (mLock) {
-                    if (getChangingUserId() != mCurrentUserId) {
-                        return;
-                    }
-                    if (readConfigurationForUserStateLocked()) {
-                        onUserStateChangedLocked();
+                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
+                    while (iterator.hasNext()) {
+                        ComponentName componentName = iterator.next();
+                        if (packageName.equals(componentName.getPackageName())) {
+                            userState.updateIfNeededLocked();
+                            return true;
+                        }
                     }
                 }
+                return false;
             }
 
             @Override
             public void onPackageRemoved(String packageName, int uid) {
                 synchronized (mLock) {
-                    if (getChangingUserId() != mCurrentUserId) {
-                        return;
-                    }
-                    Iterator<ComponentName> iterator = mEnabledServiceNames.iterator();
+                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
                     while (iterator.hasNext()) {
                         ComponentName componentName = iterator.next();
                         if (packageName.equals(componentName.getPackageName())) {
                             iterator.remove();
-                            onEnabledServiceNamesChangedLocked();
+                            persistComponentNamesToSettingLocked(
+                                    Settings.Secure.ENABLED_PRINT_SERVICES,
+                                    userState.getEnabledServices(), getChangingUserId());
+                            userState.updateIfNeededLocked();
                             return;
                         }
                     }
@@ -290,10 +226,9 @@
             public boolean onHandleForceStop(Intent intent, String[] stoppedPackages,
                     int uid, boolean doit) {
                 synchronized (mLock) {
-                    if (getChangingUserId() != mCurrentUserId) {
-                        return false;
-                    }
-                    Iterator<ComponentName> iterator = mEnabledServiceNames.iterator();
+                    UserState userState = getOrCreateUserStateLocked(getChangingUserId());
+                    boolean stoppedSomePackages = false;
+                    Iterator<ComponentName> iterator = userState.getEnabledServices().iterator();
                     while (iterator.hasNext()) {
                         ComponentName componentName = iterator.next();
                         String componentPackage = componentName.getPackageName();
@@ -302,27 +237,35 @@
                                 if (!doit) {
                                     return true;
                                 }
-                                iterator.remove();
-                                onEnabledServiceNamesChangedLocked();
+                                stoppedSomePackages = true;
+                                break;
                             }
                         }
                     }
+                    if (stoppedSomePackages) {
+                        userState.updateIfNeededLocked();
+                    }
                     return false;
                 }
             }
 
-            private void onEnabledServiceNamesChangedLocked() {
-                // Update the enabled services setting.
-                persistComponentNamesToSettingLocked(
-                        Settings.Secure.ENABLED_PRINT_SERVICES,
-                        mEnabledServiceNames, mCurrentUserId);
-                // Update the current user state.
-                onUserStateChangedLocked();
+            private void persistComponentNamesToSettingLocked(String settingName,
+                    Set<ComponentName> componentNames, int userId) {
+                StringBuilder builder = new StringBuilder();
+                for (ComponentName componentName : componentNames) {
+                    if (builder.length() > 0) {
+                        builder.append(COMPONENT_NAME_SEPARATOR);
+                    }
+                    builder.append(componentName.flattenToShortString());
+                }
+                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                        settingName, builder.toString(), userId);
             }
         };
 
         // package changes
-        monitor.register(mContext, null,  UserHandle.ALL, true);
+        monitor.register(mContext, BackgroundThread.getHandler().getLooper(),
+                UserHandle.ALL, true);
 
         // user changes
         IntentFilter intentFilter = new IntentFilter();
@@ -334,179 +277,67 @@
                 String action = intent.getAction();
                 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                     switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
+                } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+                    removeUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0));
                 }
             }
-        }, UserHandle.ALL, intentFilter, null, null);
+        }, UserHandle.ALL, intentFilter, null, BackgroundThread.getHandler());
     }
 
-    private void throwIfCallerNotSignedWithSystemKey() {
-        if (mContext.getPackageManager().checkSignatures(
-                mMyUid, Binder.getCallingUid()) != PackageManager.SIGNATURE_MATCH) {
-            throw new SecurityException("Caller must be signed with the system key!");
+    private UserState getCurrentUserStateLocked() {
+        return getOrCreateUserStateLocked(mCurrentUserId);
+    }
+
+    private UserState getOrCreateUserStateLocked(int userId) {
+        UserState userState = mUserStates.get(userId);
+        if (userState == null) {
+            userState = new UserState(mContext, userId, mLock);
+            mUserStates.put(userId, userState);
         }
-    }
-
-    private void onUserStateChangedLocked() {
-        manageServicesLocked();
-    }
-
-    private void manageServicesLocked() {
-        final int installedCount = mInstalledServices.size();
-        for (int i = 0; i < installedCount; i++) {
-            ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo();
-            ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName,
-                    resolveInfo.serviceInfo.name);
-            if (mEnabledServiceNames.contains(serviceName)) {
-                if (!mServices.containsKey(serviceName)) {
-                    new PrintServiceClient(serviceName, mCurrentUserId).ensureBoundLocked();
-                }
-            } else {
-                PrintServiceClient service = mServices.get(serviceName);
-                if (service != null) {
-                    service.ensureUnboundLocked();
-                }
-            }
-        }
-    }
-
-    private boolean readConfigurationForUserStateLocked() {
-        boolean somethingChanged = false;
-        somethingChanged |= readInstalledPrintServiceLocked();
-        somethingChanged |= readEnabledPrintServicesChangedLocked();
-        return somethingChanged;
-    }
-
-    private boolean readEnabledPrintServicesChangedLocked() {
-        Set<ComponentName> tempEnabledServiceNameSet = new HashSet<ComponentName>();
-        readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_PRINT_SERVICES,
-            mCurrentUserId, tempEnabledServiceNameSet);
-        if (!tempEnabledServiceNameSet.equals(mEnabledServiceNames)) {
-            mEnabledServiceNames.clear();
-            mEnabledServiceNames.addAll(tempEnabledServiceNameSet);
-            return true;
-        }
-        return false;
-    }
-
-    private boolean readInstalledPrintServiceLocked() {
-        Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
-
-        List<ResolveInfo> installedServices = mContext.getPackageManager()
-                .queryIntentServicesAsUser(
-                        new Intent(android.printservice.PrintService.SERVICE_INTERFACE),
-                        PackageManager.GET_SERVICES | PackageManager.GET_META_DATA,
-                        mCurrentUserId);
-
-        final int installedCount = installedServices.size();
-        for (int i = 0, count = installedCount; i < count; i++) {
-            ResolveInfo installedService = installedServices.get(i);
-            if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals(
-                    installedService.serviceInfo.permission)) {
-                ComponentName serviceName = new ComponentName(
-                        installedService.serviceInfo.packageName,
-                        installedService.serviceInfo.name);
-                Slog.w(LOG_TAG, "Skipping print service "
-                        + serviceName.flattenToShortString()
-                        + " since it does not require permission "
-                        + android.Manifest.permission.BIND_PRINT_SERVICE);
-                continue;
-            }
-            tempPrintServices.add(PrintServiceInfo.create(installedService, mContext));
-        }
-
-        if (!tempPrintServices.equals(mInstalledServices)) {
-            mInstalledServices.clear();
-            mInstalledServices.addAll(tempPrintServices);
-            return true;
-        }
-        return false;
-    }
-
-    private void readComponentNamesFromSettingLocked(String settingName, int userId,
-            Set<ComponentName> outComponentNames) {
-        String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
-                settingName, userId);
-        outComponentNames.clear();
-        if (!TextUtils.isEmpty(settingValue)) {
-            TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
-            splitter.setString(settingValue);
-            while (splitter.hasNext()) {
-                String string = splitter.next();
-                if (TextUtils.isEmpty(string)) {
-                    continue;
-                }
-                ComponentName componentName = ComponentName.unflattenFromString(string);
-                if (componentName != null) {
-                    outComponentNames.add(componentName);
-                }
-            }
-        }
-    }
-
-    private void persistComponentNamesToSettingLocked(String settingName,
-            Set<ComponentName> componentNames, int userId) {
-        StringBuilder builder = new StringBuilder();
-        for (ComponentName componentName : componentNames) {
-            if (builder.length() > 0) {
-                builder.append(COMPONENT_NAME_SEPARATOR);
-            }
-            builder.append(componentName.flattenToShortString());
-        }
-        Settings.Secure.putStringForUser(mContext.getContentResolver(),
-                settingName, builder.toString(), userId);
+        return userState;
     }
 
     private void switchUser(int newUserId) {
         synchronized (mLock) {
-            // Disconnect services for the old user.
-            mEnabledServiceNames.clear();
-            onUserStateChangedLocked();
-
-            // The user changed.
+            if (newUserId == mCurrentUserId) {
+                return;
+            }
             mCurrentUserId = newUserId;
-
-            // Update the user state based on current settings.
-            readConfigurationForUserStateLocked();
-            onUserStateChangedLocked();
-        }
-
-        // Unbind the spooler for the old user).
-        mSpooler.unbind();
-
-        // If we have queued jobs, advertise it, or we do
-        // not need the spooler for now.
-        if (notifyQueuedPrintJobs()) {
-            mSpooler.unbind();
+            UserState userState = getCurrentUserStateLocked();
+            userState.updateIfNeededLocked();
+            userState.getSpoolerLocked().notifyClientForActivteJobs();
         }
     }
 
-    private boolean notifyQueuedPrintJobs() {
-        Map<PrintServiceClient, List<PrintJobInfo>> notifications =
-                new HashMap<PrintServiceClient, List<PrintJobInfo>>();
+    private void removeUser(int removedUserId) {
         synchronized (mLock) {
-            for (PrintServiceClient service : mServices.values()) {
-                List<PrintJobInfo> printJobs = mSpooler.getPrintJobs(
-                        service.mComponentName, PrintJobInfo.STATE_QUEUED,
-                        PrintManager.APP_ID_ANY, service.mUserId);
-                notifications.put(service, printJobs);
+            UserState userState = mUserStates.get(removedUserId);
+            if (userState != null) {
+                userState.destroyLocked();
+                mUserStates.remove(removedUserId);
             }
         }
-        if (notifications.isEmpty()) {
-            return false;
-        }
-        for (Map.Entry<PrintServiceClient, List<PrintJobInfo>> notification
-                : notifications.entrySet()) {
-            PrintServiceClient service = notification.getKey();
-            List<PrintJobInfo> printJobs = notification.getValue();
-            final int printJobIdCount = printJobs.size();
-            for (int i = 0; i < printJobIdCount; i++) {
-                service.notifyPrintJobQueued(printJobs.get(i));
-            }
-        }
-        return true;
     }
 
-    private int resolveCallingUserEnforcingPermissionsIdLocked(int userId) {
+    private int resolveCallingAppEnforcingPermissions(int appId) {
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid == 0 || callingUid == Process.SYSTEM_UID
+                || callingUid == Process.SHELL_UID) {
+            return appId;
+        }
+        final int callingAppId = UserHandle.getAppId(callingUid);
+        if (appId == callingAppId) {
+            return appId;
+        }
+        if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS)
+                != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException("Call from app " + callingAppId + " as app "
+                    + appId + " without permission ACCESS_ALL_PRINT_JOBS");
+        }
+        return appId;
+    }
+
+    private int resolveCallingUserEnforcingPermissions(int userId) {
         final int callingUid = Binder.getCallingUid();
         if (callingUid == 0 || callingUid == Process.SYSTEM_UID
                 || callingUid == Process.SHELL_UID) {
@@ -524,8 +355,8 @@
                 return callingUserId;
             }
             throw new SecurityException("Call from user " + callingUserId + " as user "
-                    + userId + " without permission INTERACT_ACROSS_USERS or "
-                    + "INTERACT_ACROSS_USERS_FULL not allowed.");
+                + userId + " without permission INTERACT_ACROSS_USERS or "
+                + "INTERACT_ACROSS_USERS_FULL not allowed.");
         }
         if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
             return mCurrentUserId;
@@ -533,257 +364,4 @@
         throw new IllegalArgumentException("Calling user can be changed to only "
                 + "UserHandle.USER_CURRENT or UserHandle.USER_CURRENT_OR_SELF.");
     }
-
-    private int resolveCallingAppEnforcingPermissionsLocked(int appId) {
-        final int callingUid = Binder.getCallingUid();
-        if (callingUid == 0 || callingUid == Process.SYSTEM_UID
-                || callingUid == Process.SHELL_UID) {
-            return appId;
-        }
-        final int callingAppId = UserHandle.getAppId(callingUid);
-        if (appId == callingAppId) {
-            return appId;
-        }
-        if (mContext.checkCallingPermission(Manifest.permission.ACCESS_ALL_PRINT_JOBS)
-                != PackageManager.PERMISSION_GRANTED) {
-            throw new SecurityException("Call from app " + callingAppId + " as app "
-                    + appId + " without permission INTERACT_ACROSS_APPS");
-        }
-        return appId;
-    }
-
-    private final class PrintServiceClient extends IPrintServiceClient.Stub
-            implements ServiceConnection, DeathRecipient {
-
-        private final ComponentName mComponentName;
-
-        private final Intent mIntent;
-
-        private final int mUserId;
-
-        private IPrintService mInterface;
-
-        private boolean mBinding;
-
-        private boolean mWasConnectedAndDied;
-
-        public PrintServiceClient(ComponentName componentName, int userId) {
-            mComponentName = componentName;
-            mIntent = new Intent().setComponent(mComponentName);
-            mUserId = userId;
-        }
-
-        @Override
-        public List<PrintJobInfo> getPrintJobs() {
-            return mSpooler.getPrintJobs(mComponentName, PrintJobInfo.STATE_ANY,
-                    PrintManager.APP_ID_ANY, mUserId);
-        }
-
-        @Override
-        public PrintJobInfo getPrintJob(int printJobId) {
-            return mSpooler.getPrintJobInfo(printJobId,
-                    PrintManager.APP_ID_ANY, mUserId);
-        }
-
-        @Override
-        public boolean setPrintJobState(int printJobId, int state) {
-            return mSpooler.setPrintJobState(printJobId, state, mUserId);
-        }
-
-        @Override
-        public boolean setPrintJobTag(int printJobId, String tag) {
-            return mSpooler.setPrintJobTag(printJobId, tag, mUserId);
-        }
-
-        @Override
-        public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
-            mSpooler.writePrintJobData(fd, printJobId, mUserId);
-        }
-
-        @Override
-        public void addDiscoveredPrinters(List<PrinterInfo> printers) {
-            throwIfPrinterIdsForPrinterInfoTampered(printers);
-            synchronized (mLock) {
-                if (mPrinterDiscoveryObserver != null) {
-                    try {
-                        mPrinterDiscoveryObserver.addDiscoveredPrinters(printers);
-                    } catch (RemoteException re) {
-                        /* ignore */
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void removeDiscoveredPrinters(List<PrinterId> printerIds) {
-            throwIfPrinterIdsTampered(printerIds);
-            synchronized (mLock) {
-                if (mPrinterDiscoveryObserver != null) {
-                    try {
-                        mPrinterDiscoveryObserver.removeDiscoveredPrinters(printerIds);
-                    } catch (RemoteException re) {
-                        /* ignore */
-                    }
-                }
-            }
-        }
-
-        public void requestCancelPrintJob(PrintJobInfo printJob) {
-            synchronized (mLock) {
-                try {
-                    mInterface.requestCancelPrintJob(printJob);
-                } catch (RemoteException re) {
-                    Slog.e(LOG_TAG, "Error canceling pring job!", re);
-                }
-            }
-        }
-
-        public void notifyPrintJobQueued(PrintJobInfo printJob) {
-            IPrintService service = mInterface;
-            if (service != null) {
-                try {
-                    service.onPrintJobQueued(printJob);
-                } catch (RemoteException re) {
-                    /* ignore */
-                }
-            }
-        }
-
-        public void startPrinterDiscovery() {
-            IPrintService service = mInterface;
-            if (service != null) {
-                try {
-                    service.startPrinterDiscovery();
-                } catch (RemoteException re) {
-                    /* ignore */
-                }
-            }
-        }
-
-        public void stopPrintersDiscovery() {
-            IPrintService service = mInterface;
-            if (service != null) {
-                try {
-                    service.stopPrinterDiscovery();
-                } catch (RemoteException re) {
-                    /* ignore */
-                }
-            }
-        }
-
-        public void ensureBoundLocked() {
-            if (mBinding) {
-                return;
-            }
-            if (mInterface == null) {
-                mBinding = true;
-                mContext.bindServiceAsUser(mIntent, this,
-                        Context.BIND_AUTO_CREATE, new UserHandle(mUserId));
-            }
-        }
-
-        public void ensureUnboundLocked() {
-            if (mBinding) {
-                mBinding = false;
-                return;
-            }
-            if (mInterface != null) {
-                mContext.unbindService(this);
-                destroyLocked();
-            }
-        }
-
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            synchronized (mLock) {
-                mInterface = IPrintService.Stub.asInterface(service);
-                mServices.put(mComponentName, this);
-                try {
-                    mInterface.asBinder().linkToDeath(this, 0);
-                } catch (RemoteException re) {
-                    destroyLocked();
-                    return;
-                }
-                if (mUserId != mCurrentUserId) {
-                    destroyLocked();
-                    return;
-                }
-                if (mBinding || mWasConnectedAndDied) {
-                    mBinding = false;
-                    mWasConnectedAndDied = false;
-                    onUserStateChangedLocked();
-                    try {
-                        mInterface.setClient(this);
-                    } catch (RemoteException re) {
-                        Slog.w(LOG_TAG, "Error while setting client for service: "
-                                + service, re);
-                    }
-                } else {
-                    destroyLocked();
-                }
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            /* do nothing - #binderDied takes care */
-        }
-
-        @Override
-        public void binderDied() {
-            synchronized (mLock) {
-                if (isConnectedLocked()) {
-                    mWasConnectedAndDied = true;
-                }
-                destroyLocked();
-            }
-        }
-
-        private void destroyLocked() {
-            if (mServices.remove(mComponentName) == null) {
-                return;
-            }
-            if (isConnectedLocked()) {
-                try {
-                    mInterface.asBinder().unlinkToDeath(this, 0);
-                } catch (NoSuchElementException nse) {
-                    /* ignore */
-                }
-                try {
-                    mInterface.setClient(null);
-                } catch (RemoteException re) {
-                    /* ignore */
-                }
-                mInterface = null;
-            }
-            mBinding = false;
-        }
-
-        private boolean isConnectedLocked() {
-            return (mInterface != null);
-        }
-
-        private void throwIfPrinterIdsForPrinterInfoTampered(List<PrinterInfo> printerInfos) {
-            final int printerInfoCount = printerInfos.size();
-            for (int i = 0; i < printerInfoCount; i++) {
-                PrinterId printerId = printerInfos.get(i).getId();
-                throwIfPrinterIdTampered(printerId);
-            }
-        }
-
-        private void throwIfPrinterIdsTampered(List<PrinterId> printerIds) {
-            final int printerIdCount = printerIds.size();
-            for (int i = 0; i < printerIdCount; i++) {
-                PrinterId printerId = printerIds.get(i);
-                throwIfPrinterIdTampered(printerId);
-            }
-        }
-
-        private void throwIfPrinterIdTampered(PrinterId printerId) {
-            if (printerId == null || printerId.getServiceComponentName() == null
-                    || !printerId.getServiceComponentName().equals(mComponentName)) {
-                throw new IllegalArgumentException("Invalid printer id: " + printerId);
-            }
-        }
-    }
 }
diff --git a/services/java/com/android/server/print/RemotePrintService.java b/services/java/com/android/server/print/RemotePrintService.java
new file mode 100644
index 0000000..b9e0280
--- /dev/null
+++ b/services/java/com/android/server/print/RemotePrintService.java
@@ -0,0 +1,466 @@
+/*
+ * 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.server.print;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrintJobInfo;
+import android.print.PrintManager;
+import android.print.PrinterId;
+import android.print.PrinterInfo;
+import android.printservice.IPrintService;
+import android.printservice.IPrintServiceClient;
+import android.util.Slog;
+
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a remote print service. It abstracts away the binding
+ * and unbinding from the remote implementation. Clients can call methods of
+ * this class without worrying about when and how to bind/unbind.
+ */
+final class RemotePrintService {
+
+    private static final String LOG_TAG = "RemotePrintService";
+
+    private static final boolean DEBUG = true;
+
+    private final Context mContext;
+
+    private final ComponentName mComponentName;
+
+    private final Intent mIntent;
+
+    private final RemotePrintSpooler mSpooler;
+
+    private final int mUserId;
+
+    private final List<Runnable> mPendingCommands = new ArrayList<Runnable>();
+
+    private final ServiceConnection mServiceConnection = new RemoteServiceConneciton();
+
+    private final RemotePrintServiceClient mPrintServiceClient;
+
+    private final Handler mHandler;
+
+    private IPrintService mPrintService;
+
+    private boolean mBinding;
+
+    private boolean mDestroyed;
+
+    public RemotePrintService(Context context, ComponentName componentName, int userId,
+            RemotePrintSpooler spooler) {
+        mContext = context;
+        mComponentName = componentName;
+        mIntent = new Intent().setComponent(mComponentName);
+        mUserId = userId;
+        mSpooler = spooler;
+        mHandler = new MyHandler(context.getMainLooper());
+        mPrintServiceClient = new RemotePrintServiceClient(this);
+    }
+
+    public void destroy() {
+        mHandler.sendEmptyMessage(MyHandler.MSG_DESTROY);
+    }
+
+    private void handleDestroy() {
+        throwIfDestroyed();
+        ensureUnbound();
+        mDestroyed = true;
+    }
+
+    public void onAllPrintJobsHandled() {
+        mHandler.sendEmptyMessage(MyHandler.MSG_ALL_PRINT_JOBS_HANDLED);
+    }
+
+    private void handleOnAllPrintJobsHandled() {
+        throwIfDestroyed();
+        if (isBound()) {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnAllPrintJobsHandled");
+            }
+            // If bound and all the work is completed, then unbind.
+            ensureUnbound();
+        }
+    }
+
+    public void onRequestCancelPrintJob(PrintJobInfo printJob) {
+        mHandler.obtainMessage(MyHandler.MSG_REQUEST_CANCEL_PRINT_JOB,
+                printJob).sendToTarget();
+    }
+
+    private void handleOnRequestCancelPrintJob(final PrintJobInfo printJob) {
+        throwIfDestroyed();
+        // If we are not bound, then we have no print jobs to handle
+        // which means that there are no print jobs to be cancelled.
+        if (isBound()) {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnRequestCancelPrintJob()");
+            }
+            try {
+                mPrintService.requestCancelPrintJob(printJob);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error canceling pring job.", re);
+            }
+        }
+    }
+
+    public void onPrintJobQueued(PrintJobInfo printJob) {
+        mHandler.obtainMessage(MyHandler.MSG_PRINT_JOB_QUEUED,
+                printJob).sendToTarget();
+    }
+
+    private void handleOnPrintJobQueued(final PrintJobInfo printJob) {
+        throwIfDestroyed();
+        if (!isBound()) {
+            ensureBound();
+            mPendingCommands.add(new Runnable() {
+                @Override
+                 public void run() {
+                    handleOnPrintJobQueued(printJob);
+                }
+            });
+        } else {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "[user: " + mUserId + "] handleOnPrintJobQueued()");
+            }
+            try {
+                mPrintService.onPrintJobQueued(printJob);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error announcing queued pring job.", re);
+            }
+        }
+    }
+
+    public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+        mHandler.obtainMessage(MyHandler.MSG_START_PRINTER_DISCOVERY, observer).sendToTarget();
+    }
+
+    private void handleOnStartPrinterDiscovery(final IPrinterDiscoveryObserver observer) {
+        throwIfDestroyed();
+        if (!isBound()) {
+            ensureBound();
+            mPendingCommands.add(new Runnable() {
+                @Override
+                public void run() {
+                    handleOnStartPrinterDiscovery(observer);
+                }
+            });
+        } else {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "[user: " + mUserId + "] onStartPrinterDiscovery()");
+            }
+            try {
+                mPrintService.startPrinterDiscovery(observer);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error announcing start printer dicovery.", re);
+            }
+        }
+    }
+
+    public void onStopPrinterDiscovery() {
+        mHandler.sendEmptyMessage(MyHandler.MSG_STOP_PRINTER_DISCOVERY);
+    }
+
+    private void handleStopPrinterDiscovery() {
+        throwIfDestroyed();
+        if (!isBound()) {
+            ensureBound();
+            mPendingCommands.add(new Runnable() {
+                @Override
+                public void run() {
+                    handleStopPrinterDiscovery();
+                }
+            });
+        } else {
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "[user: " + mUserId + "] onStopPrinterDiscovery()");
+            }
+            try {
+                mPrintService.stopPrinterDiscovery();
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error announcing stop printer dicovery.", re);
+            }
+        }
+    }
+
+    private boolean isBound() {
+        return mPrintService != null;
+    }
+
+    private void ensureBound() {
+        if (isBound() || mBinding) {
+            return;
+        }
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureBound()");
+        }
+        mBinding = true;
+        mContext.bindServiceAsUser(mIntent, mServiceConnection,
+                Context.BIND_AUTO_CREATE, new UserHandle(mUserId));
+    }
+
+    private void ensureUnbound() {
+        if (!isBound() && !mBinding) {
+            return;
+        }
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserId + "] ensureUnbound()");
+        }
+        mBinding = false;
+        mPendingCommands.clear();
+        if (isBound()) {
+            try {
+                mPrintService.setClient(null);
+            } catch (RemoteException re) {
+                /* ignore */
+            }
+            mPrintService = null;
+            mContext.unbindService(mServiceConnection);
+        }
+    }
+
+    private void throwIfDestroyed() {
+        if (mDestroyed) {
+            throw new IllegalStateException("Cannot interact with a destroyed service");
+        }
+    }
+
+    private class RemoteServiceConneciton implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            if (mDestroyed || !mBinding) {
+                return;
+            }
+            mBinding = false;
+            mPrintService = IPrintService.Stub.asInterface(service);
+            try {
+                mPrintService.setClient(mPrintServiceClient);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error setting client for: " + service, re);
+                handleDestroy();
+                return;
+            }
+            final int pendingCommandCount = mPendingCommands.size();
+            for (int i = 0; i < pendingCommandCount; i++) {
+                Runnable pendingCommand = mPendingCommands.get(i);
+                pendingCommand.run();
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            mBinding = true;
+        }
+    }
+
+    private final class MyHandler extends Handler {
+        public static final int MSG_ALL_PRINT_JOBS_HANDLED = 1;
+        public static final int MSG_REQUEST_CANCEL_PRINT_JOB = 2;
+        public static final int MSG_PRINT_JOB_QUEUED = 3;
+        public static final int MSG_START_PRINTER_DISCOVERY = 4;
+        public static final int MSG_STOP_PRINTER_DISCOVERY = 5;
+        public static final int MSG_DESTROY = 6;
+
+        public MyHandler(Looper looper) {
+            super(looper, null, false);
+        }
+
+        @Override
+        public void handleMessage(Message message) {
+            switch (message.what) {
+                case MSG_ALL_PRINT_JOBS_HANDLED: {
+                    handleOnAllPrintJobsHandled();
+                } break;
+
+                case MSG_REQUEST_CANCEL_PRINT_JOB: {
+                    PrintJobInfo printJob = (PrintJobInfo) message.obj;
+                    handleOnRequestCancelPrintJob(printJob);
+                } break;
+
+                case MSG_PRINT_JOB_QUEUED: {
+                    PrintJobInfo printJob = (PrintJobInfo) message.obj;
+                    handleOnPrintJobQueued(printJob);
+                } break;
+
+                case MSG_START_PRINTER_DISCOVERY: {
+                    IPrinterDiscoveryObserver observer = (IPrinterDiscoveryObserver) message.obj;
+                    handleOnStartPrinterDiscovery(new SecurePrinterDiscoveryObserver(
+                            mComponentName, observer));
+                } break;
+
+                case MSG_STOP_PRINTER_DISCOVERY: {
+                    handleStopPrinterDiscovery();
+                } break;
+
+                case MSG_DESTROY: {
+                    handleDestroy();
+                } break;
+            }
+        }
+    }
+
+    private static final class RemotePrintServiceClient extends IPrintServiceClient.Stub {
+        private final WeakReference<RemotePrintService> mWeakService;
+
+        public RemotePrintServiceClient(RemotePrintService service) {
+            mWeakService = new WeakReference<RemotePrintService>(service);
+        }
+
+        @Override
+        public List<PrintJobInfo> getPrintJobInfos() {
+            RemotePrintService service = mWeakService.get();
+            if (service != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    return service.mSpooler.getPrintJobInfos(service.mComponentName,
+                            PrintJobInfo.STATE_ANY, PrintManager.APP_ID_ANY);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+            return null;
+        }
+
+        @Override
+        public PrintJobInfo getPrintJobInfo(int printJobId) {
+            RemotePrintService service = mWeakService.get();
+            if (service != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    return service.mSpooler.getPrintJobInfo(printJobId,
+                            PrintManager.APP_ID_ANY);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+            return null;
+        }
+
+        @Override
+        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);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public boolean setPrintJobTag(int printJobId, String tag) {
+            RemotePrintService service = mWeakService.get();
+            if (service != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    return service.mSpooler.setPrintJobTag(printJobId, tag);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
+            RemotePrintService service = mWeakService.get();
+            if (service != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    service.mSpooler.writePrintJobData(fd, printJobId);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+    }
+
+    private static final class SecurePrinterDiscoveryObserver
+            extends IPrinterDiscoveryObserver.Stub {
+        private final ComponentName mComponentName;
+
+        private final IPrinterDiscoveryObserver mDecoratedObsever;
+
+        public SecurePrinterDiscoveryObserver(ComponentName componentName,
+                IPrinterDiscoveryObserver observer) {
+            mComponentName = componentName;
+            mDecoratedObsever = observer;
+        }
+
+        @Override
+        public void addDiscoveredPrinters(List<PrinterInfo> printers) {
+            throwIfPrinterIdsForPrinterInfoTampered(printers);
+            try {
+                mDecoratedObsever.addDiscoveredPrinters(printers);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error delegating to addDiscoveredPrinters", re);
+            }
+        }
+
+        @Override
+        public void removeDiscoveredPrinters(List<PrinterId> printerIds) {
+            throwIfPrinterIdsTampered(printerIds);
+            try {
+                mDecoratedObsever.removeDiscoveredPrinters(printerIds);
+            } catch (RemoteException re) {
+                Slog.e(LOG_TAG, "Error delegating to removeDiscoveredPrinters", re);
+            }
+        }
+
+        private void throwIfPrinterIdsForPrinterInfoTampered(
+                List<PrinterInfo> printerInfos) {
+            final int printerInfoCount = printerInfos.size();
+            for (int i = 0; i < printerInfoCount; i++) {
+                PrinterId printerId = printerInfos.get(i).getId();
+                throwIfPrinterIdTampered(printerId);
+            }
+        }
+
+        private void throwIfPrinterIdsTampered(List<PrinterId> printerIds) {
+            final int printerIdCount = printerIds.size();
+            for (int i = 0; i < printerIdCount; i++) {
+                PrinterId printerId = printerIds.get(i);
+                throwIfPrinterIdTampered(printerId);
+            }
+        }
+
+        private void throwIfPrinterIdTampered(PrinterId printerId) {
+            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
new file mode 100644
index 0000000..bf2c8e7
--- /dev/null
+++ b/services/java/com/android/server/print/RemotePrintSpooler.java
@@ -0,0 +1,613 @@
+/*
+ * 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.server.print;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.print.IPrintClient;
+import android.print.IPrintDocumentAdapter;
+import android.print.IPrintSpooler;
+import android.print.IPrintSpoolerCallbacks;
+import android.print.IPrintSpoolerClient;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrintAttributes;
+import android.print.PrintJobInfo;
+import android.util.Slog;
+import android.util.TimedRemoteCaller;
+
+import libcore.io.IoUtils;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * This represents the remote print spooler as a local object to the
+ * PrintManagerSerivce. It is responsible to connecting to the remote
+ * spooler if needed, to make the timed remote calls, to handle
+ * remote exceptions, and to bind/unbind to the remote instance as
+ * needed.
+ */
+final class RemotePrintSpooler {
+
+    private static final String LOG_TAG = "RemotePrintSpooler";
+
+    private static final boolean DEBUG = true;
+
+    private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000;
+
+    private final Object mLock = new Object();
+
+    private final GetPrintJobInfosCaller mGetPrintJobInfosCaller = new GetPrintJobInfosCaller();
+
+    private final CreatePrintJobCaller mCreatePrintJobCaller = new CreatePrintJobCaller();
+
+    private final CancelPrintJobCaller mCancelPrintJobCaller = new CancelPrintJobCaller();
+
+    private final GetPrintJobInfoCaller mGetPrintJobInfoCaller = new GetPrintJobInfoCaller();
+
+    private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller();
+
+    private final SetPrintJobTagCaller mSetPrintJobTagCaller = new SetPrintJobTagCaller();
+
+    private final ServiceConnection mServiceConnection = new MyServiceConnection();
+
+    private final Context mContext;
+
+    private final UserHandle mUserHandle;
+
+    private final PrintSpoolerClient mClient;
+
+    private final Intent mIntent;
+
+    private final PrintSpoolerCallbacks mCallbacks;
+
+    private IPrintSpooler mRemoteInstance;
+
+    private boolean mDestroyed;
+
+    public static interface PrintSpoolerCallbacks {
+        public void onPrintJobQueued(PrintJobInfo printJob);
+        public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer);
+        public void onStopPrinterDiscovery();
+        public void onAllPrintJobsForServiceHandled(ComponentName printService);
+    }
+
+    public RemotePrintSpooler(Context context, int userId,
+            PrintSpoolerCallbacks callbacks) {
+        mContext = context;
+        mUserHandle = new UserHandle(userId);
+        mCallbacks = callbacks;
+        mClient = new PrintSpoolerClient(this);
+        mIntent = new Intent();
+        mIntent.setComponent(new ComponentName("com.android.printspooler",
+                "com.android.printspooler.PrintSpoolerService"));
+    }
+
+    public final List<PrintJobInfo> getPrintJobInfos(ComponentName componentName, int state,
+            int appId) {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+        }
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfos()");
+        }
+        try {
+            return mGetPrintJobInfosCaller.getPrintJobInfos(getRemoteInstanceLazy(),
+                    componentName, state, appId);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error getting print jobs.", re);
+        } catch (TimeoutException te) {
+            Slog.e(LOG_TAG, "Error getting print jobs.", te);
+        }
+        return null;
+    }
+
+    public final PrintJobInfo createPrintJob(String printJobName, IPrintClient client,
+            IPrintDocumentAdapter documentAdapter, PrintAttributes attributes, int appId) {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+        }
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] createPrintJob()");
+        }
+        try {
+            return mCreatePrintJobCaller.createPrintJob(getRemoteInstanceLazy(),
+                    printJobName, client, documentAdapter, attributes, appId);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error creating print job.", re);
+        } catch (TimeoutException te) {
+            Slog.e(LOG_TAG, "Error creating print job.", te);
+        }
+        return null;
+    }
+
+    public final boolean cancelPrintJob(int printJobId, int appId) {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+        }
+        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);
+        }
+        return false;
+    }
+
+    public final void writePrintJobData(ParcelFileDescriptor fd, int printJobId) {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+        }
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] writePrintJobData()");
+        }
+        try {
+            getRemoteInstanceLazy().writePrintJobData(fd, printJobId);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error writing print job data.", re);
+        } catch (TimeoutException te) {
+            Slog.e(LOG_TAG, "Error writing print job data.", te);
+        } finally {
+            // We passed the file descriptor across and now the other
+            // side is responsible to close it, so close the local copy.
+            IoUtils.closeQuietly(fd);
+        }
+    }
+
+    public final PrintJobInfo getPrintJobInfo(int printJobId, int appId) {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+        }
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] getPrintJobInfo()");
+        }
+        try {
+            return mGetPrintJobInfoCaller.getPrintJobInfo(getRemoteInstanceLazy(),
+                    printJobId, appId);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error getting print job info.", re);
+        } catch (TimeoutException te) {
+            Slog.e(LOG_TAG, "Error getting print job info.", te);
+        }
+        return null;
+    }
+
+    public final boolean setPrintJobState(int printJobId, int state) {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+        }
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobState()");
+        }
+        try {
+            return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstanceLazy(),
+                    printJobId, state);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error setting print job state.", re);
+        } catch (TimeoutException te) {
+            Slog.e(LOG_TAG, "Error setting print job state.", te);
+        }
+        return false;
+    }
+
+    public final boolean setPrintJobTag(int printJobId, String tag) {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+        }
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] setPrintJobTag()");
+        }
+        try {
+            return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstanceLazy(),
+                    printJobId, tag);
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error setting print job tag.", re);
+        } catch (TimeoutException te) {
+            Slog.e(LOG_TAG, "Error setting print job tag.", te);
+        }
+        return false;
+    }
+
+    public final void notifyClientForActivteJobs() {
+        throwIfCalledOnMainThread();
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+        }
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier()
+                    + "] notifyClientForActivteJobs()");
+        }
+        try {
+            getRemoteInstanceLazy().notifyClientForActivteJobs();
+        } catch (RemoteException re) {
+            Slog.e(LOG_TAG, "Error asking for active print job notification.", re);
+        } catch (TimeoutException te) {
+            Slog.e(LOG_TAG, "Error asking for active print job notification.", te);
+        }
+    }
+
+    public final void destroy() {
+        throwIfCalledOnMainThread();
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] destroy()");
+        }
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            unbindLocked();
+            mDestroyed = true;
+        }
+    }
+
+    private void onAllPrintJobsHandled() {
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            unbindLocked();
+        }
+    }
+
+    private IPrintSpooler getRemoteInstanceLazy() throws TimeoutException {
+        synchronized (mLock) {
+            if (mRemoteInstance != null) {
+                return mRemoteInstance;
+            }
+            bindLocked();
+            return mRemoteInstance;
+        }
+    }
+
+    private void bindLocked() throws TimeoutException {
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] bindLocked()");
+        }
+
+        mContext.bindServiceAsUser(mIntent, mServiceConnection,
+                Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT,
+                    mUserHandle);
+
+        final long startMillis = SystemClock.uptimeMillis();
+        while (true) {
+            if (mRemoteInstance != null) {
+                break;
+            }
+            final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
+            final long remainingMillis = BIND_SPOOLER_SERVICE_TIMEOUT - elapsedMillis;
+            if (remainingMillis <= 0) {
+                throw new TimeoutException("Cannot get spooler!");
+            }
+            try {
+                mLock.wait(remainingMillis);
+            } catch (InterruptedException ie) {
+                /* ignore */
+            }
+        }
+    }
+
+    private void unbindLocked() {
+        if (DEBUG) {
+            Slog.i(LOG_TAG, "[user: " + mUserHandle.getIdentifier() + "] unbindLocked()");
+        }
+        clearClientLocked();
+        mRemoteInstance = null;
+        mContext.unbindService(mServiceConnection);
+    }
+
+    private void setClientLocked() {
+        try {
+            mRemoteInstance.setClient(mClient);
+        } catch (RemoteException re) {
+            Slog.d(LOG_TAG, "Error setting print spooler client", re);
+        }
+    }
+
+    private void clearClientLocked() {
+        try {
+            mRemoteInstance.setClient(null);
+        } catch (RemoteException re) {
+            Slog.d(LOG_TAG, "Error clearing print spooler client", re);
+        }
+
+    }
+
+    private void throwIfDestroyedLocked() {
+        if (mDestroyed) {
+            throw new IllegalStateException("Cannot interact with a destroyed instance.");
+        }
+    }
+
+    private void throwIfCalledOnMainThread() {
+        if (Thread.currentThread() == mContext.getMainLooper().getThread()) {
+            throw new RuntimeException("Cannot invoke on the main thread");
+        }
+    }
+
+    private final class MyServiceConnection implements ServiceConnection {
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            synchronized (mLock) {
+                mRemoteInstance = IPrintSpooler.Stub.asInterface(service);
+                setClientLocked();
+                mLock.notifyAll();
+            }
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            synchronized (mLock) {
+                clearClientLocked();
+                mRemoteInstance = null;
+            }
+        }
+    }
+
+    private static final class GetPrintJobInfosCaller
+            extends TimedRemoteCaller<List<PrintJobInfo>> {
+        private final IPrintSpoolerCallbacks mCallback;
+
+        public GetPrintJobInfosCaller() {
+            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+            mCallback = new BasePrintSpoolerServiceCallbacks() {
+                @Override
+                public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobs, int sequence) {
+                    onRemoteMethodResult(printJobs, sequence);
+                }
+            };
+        }
+
+        public List<PrintJobInfo> getPrintJobInfos(IPrintSpooler target,
+                ComponentName componentName, int state, int appId)
+                        throws RemoteException, TimeoutException {
+            final int sequence = onBeforeRemoteCall();
+            target.getPrintJobInfos(mCallback, componentName, state, appId, sequence);
+            return getResultTimed(sequence);
+        }
+    }
+
+    private static final class CreatePrintJobCaller extends TimedRemoteCaller<PrintJobInfo> {
+        private final IPrintSpoolerCallbacks mCallback;
+
+        public CreatePrintJobCaller() {
+            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+            mCallback = new BasePrintSpoolerServiceCallbacks() {
+                @Override
+                public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
+                    onRemoteMethodResult(printJob, sequence);
+                }
+            };
+        }
+
+        public PrintJobInfo createPrintJob(IPrintSpooler target, String printJobName,
+                IPrintClient client, IPrintDocumentAdapter documentAdapter,
+                PrintAttributes attributes, int appId) throws RemoteException, TimeoutException {
+            final int sequence = onBeforeRemoteCall();
+            target.createPrintJob(printJobName, client, documentAdapter, attributes,
+                    mCallback, appId, sequence);
+            return getResultTimed(sequence);
+        }
+    }
+
+    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;
+
+        public GetPrintJobInfoCaller() {
+            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+            mCallback = new BasePrintSpoolerServiceCallbacks() {
+                @Override
+                public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
+                    onRemoteMethodResult(printJob, sequence);
+                }
+            };
+        }
+
+        public PrintJobInfo getPrintJobInfo(IPrintSpooler target, int printJobId,
+                int appId) throws RemoteException, TimeoutException {
+            final int sequence = onBeforeRemoteCall();
+            target.getPrintJobInfo(printJobId, mCallback, appId, sequence);
+            return getResultTimed(sequence);
+        }
+    }
+
+    private static final class SetPrintJobStateCaller extends TimedRemoteCaller<Boolean> {
+        private final IPrintSpoolerCallbacks mCallback;
+
+        public SetPrintJobStateCaller() {
+            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+            mCallback = new BasePrintSpoolerServiceCallbacks() {
+                @Override
+                public void onSetPrintJobStateResult(boolean success, int sequence) {
+                    onRemoteMethodResult(success, sequence);
+                }
+            };
+        }
+
+        public boolean setPrintJobState(IPrintSpooler target, int printJobId,
+                int status) throws RemoteException, TimeoutException {
+            final int sequence = onBeforeRemoteCall();
+            target.setPrintJobState(printJobId, status, mCallback, sequence);
+            return getResultTimed(sequence);
+        }
+    }
+
+    private static final class SetPrintJobTagCaller extends TimedRemoteCaller<Boolean> {
+        private final IPrintSpoolerCallbacks mCallback;
+
+        public SetPrintJobTagCaller() {
+            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
+            mCallback = new BasePrintSpoolerServiceCallbacks() {
+                @Override
+                public void onSetPrintJobTagResult(boolean success, int sequence) {
+                    onRemoteMethodResult(success, sequence);
+                }
+            };
+        }
+
+        public boolean setPrintJobTag(IPrintSpooler target, int printJobId,
+                String tag) throws RemoteException, TimeoutException {
+            final int sequence = onBeforeRemoteCall();
+            target.setPrintJobTag(printJobId, tag, mCallback, sequence);
+            return getResultTimed(sequence);
+        }
+    }
+
+    private static abstract class BasePrintSpoolerServiceCallbacks
+            extends IPrintSpoolerCallbacks.Stub {
+        @Override
+        public void onGetPrintJobInfosResult(List<PrintJobInfo> printJobIds, int sequence) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onCancelPrintJobResult(boolean canceled, int sequence) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onSetPrintJobStateResult(boolean success, int sequece) {
+            /* do nothing */
+        }
+
+        @Override
+        public void onSetPrintJobTagResult(boolean success, int sequence) {
+            /* do nothing */
+        }
+    }
+
+    private static final class PrintSpoolerClient extends IPrintSpoolerClient.Stub {
+
+        private final WeakReference<RemotePrintSpooler> mWeakSpooler;
+
+        public PrintSpoolerClient(RemotePrintSpooler spooler) {
+            mWeakSpooler = new WeakReference<RemotePrintSpooler>(spooler);
+        }
+
+        @Override
+        public void onPrintJobQueued(PrintJobInfo printJob) {
+            RemotePrintSpooler spooler = mWeakSpooler.get();
+            if (spooler != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    spooler.mCallbacks.onPrintJobQueued(printJob);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+
+        @Override
+        public void onAllPrintJobsForServiceHandled(ComponentName printService) {
+            RemotePrintSpooler spooler = mWeakSpooler.get();
+            if (spooler != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    spooler.mCallbacks.onAllPrintJobsForServiceHandled(printService);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+
+        @Override
+        public void onAllPrintJobsHandled() {
+            RemotePrintSpooler spooler = mWeakSpooler.get();
+            if (spooler != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    spooler.onAllPrintJobsHandled();
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+
+        @Override
+        public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+            RemotePrintSpooler spooler = mWeakSpooler.get();
+            if (spooler != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    spooler.mCallbacks.onStartPrinterDiscovery(observer);
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+
+        @Override
+        public void onStopPrinterDiscovery() throws RemoteException {
+            RemotePrintSpooler spooler = mWeakSpooler.get();
+            if (spooler != null) {
+                final long identity = Binder.clearCallingIdentity();
+                try {
+                    spooler.mCallbacks.onStopPrinterDiscovery();
+                } finally {
+                    Binder.restoreCallingIdentity(identity);
+                }
+            }
+        }
+    }
+}
diff --git a/services/java/com/android/server/print/RemoteSpooler.java b/services/java/com/android/server/print/RemoteSpooler.java
deleted file mode 100644
index fef5818..0000000
--- a/services/java/com/android/server/print/RemoteSpooler.java
+++ /dev/null
@@ -1,416 +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.server.print;
-
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.IBinder.DeathRecipient;
-import android.print.IPrintAdapter;
-import android.print.IPrintClient;
-import android.print.IPrintSpoolerService;
-import android.print.IPrintSpoolerServiceCallbacks;
-import android.print.PrintAttributes;
-import android.print.PrintJobInfo;
-import android.util.Slog;
-import android.util.TimedRemoteCaller;
-
-import java.io.IOException;
-import java.util.List;
-import java.util.concurrent.TimeoutException;
-
-/**
- * This represents the remote print spooler as a local object to the
- * PrintManagerSerivce. It is responsible to connecting to the remove
- * spooler if needed, to make the timed out remote calls, and to handle
- * remove exceptions.
- */
-final class RemoteSpooler implements ServiceConnection, DeathRecipient {
-
-    private static final String LOG_TAG = "Spooler";
-
-    private static final long BIND_SPOOLER_SERVICE_TIMEOUT = 10000;
-
-    private final Object mLock = new Object();
-
-    private final Context mContext;
-
-    private final Intent mIntent;
-
-    private final GetPrintJobsCaller mGetPrintJobsCaller = new GetPrintJobsCaller();
-
-    private final CreatePrintJobCaller mCreatePrintJobCaller = new CreatePrintJobCaller();
-
-    private final CancelPrintJobCaller mCancelPrintJobCaller = new CancelPrintJobCaller();
-
-    private final GetPrintJobCaller mGetPrintJobCaller = new GetPrintJobCaller();
-
-    private final SetPrintJobStateCaller mSetPrintJobStatusCaller = new SetPrintJobStateCaller();
-
-    private final SetPrintJobTagCaller mSetPrintJobTagCaller = new SetPrintJobTagCaller();
-
-    private IPrintSpoolerService mRemoteInterface;
-
-    private int mUserId = UserHandle.USER_NULL;
-
-    public RemoteSpooler(Context context) {
-        mContext = context;
-        mIntent = new Intent();
-        mIntent.setComponent(new ComponentName("com.android.printspooler",
-                "com.android.printspooler.PrintSpoolerService"));
-    }
-
-    public List<PrintJobInfo> getPrintJobs(ComponentName componentName, int state, int appId,
-            int userId) {
-        try {
-            return mGetPrintJobsCaller.getPrintJobs(getRemoteInstance(userId),
-                    componentName, state, appId);
-        } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error getting print jobs!", re);
-        } catch (TimeoutException te) {
-            Slog.e(LOG_TAG, "Error getting print jobs!", te);
-        }
-        return null;
-    }
-
-    public PrintJobInfo createPrintJob(String printJobName, IPrintClient client,
-            IPrintAdapter printAdapter, PrintAttributes attributes, int appId, int userId) {
-        try {
-            return mCreatePrintJobCaller.createPrintJob(getRemoteInstance(userId),
-                    printJobName, client, printAdapter, attributes, appId);
-        } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error creating print job!", re);
-        } catch (TimeoutException te) {
-            Slog.e(LOG_TAG, "Error creating print job!", te);
-        }
-        return null;
-    }
-
-    public boolean cancelPrintJob(int printJobId, int appId, int userId) {
-        try {
-            return mCancelPrintJobCaller.cancelPrintJob(getRemoteInstance(userId),
-                    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);
-        }
-        return false;
-    }
-
-    public void writePrintJobData(ParcelFileDescriptor fd, int printJobId, int userId) {
-        try {
-            getRemoteInstance(userId).writePrintJobData(fd, printJobId);
-        } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error writing print job data!", re);
-        } catch (TimeoutException te) {
-            Slog.e(LOG_TAG, "Error writing print job data!", te);
-        } finally {
-            // We passed the file descriptor across and now the other
-            // side is responsible to close it, so close the local copy.
-            try {
-                fd.close();
-            } catch (IOException ioe) {
-                /* ignore */
-            }
-        }
-    }
-
-    public PrintJobInfo getPrintJobInfo(int printJobId, int appId, int userId) {
-        try {
-            return mGetPrintJobCaller.getPrintJobInfo(getRemoteInstance(userId),
-                    printJobId, appId);
-        } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error getting print job!", re);
-        } catch (TimeoutException te) {
-            Slog.e(LOG_TAG, "Error getting print job!", te);
-        }
-        return null;
-    }
-
-    public boolean setPrintJobState(int printJobId, int state, int userId) {
-        try {
-            return mSetPrintJobStatusCaller.setPrintJobState(getRemoteInstance(userId),
-                    printJobId, state);
-        } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error setting print job status!", re);
-        } catch (TimeoutException te) {
-            Slog.e(LOG_TAG, "Error setting print job status!", te);
-        }
-        return false;
-    }
-
-    public boolean setPrintJobTag(int printJobId, String tag, int userId) {
-        try {
-            return mSetPrintJobTagCaller.setPrintJobTag(getRemoteInstance(userId),
-                    printJobId, tag);
-        } catch (RemoteException re) {
-            Slog.e(LOG_TAG, "Error setting print job tag!", re);
-        } catch (TimeoutException te) {
-            Slog.e(LOG_TAG, "Error setting print job tag!", te);
-        }
-        return false;
-    }
-
-    @Override
-    public void onServiceDisconnected(ComponentName name) {
-        binderDied();
-    }
-
-    @Override
-    public void onServiceConnected(ComponentName name, IBinder service) {
-        synchronized (mLock) {
-            try {
-                service.linkToDeath(this, 0);
-                mRemoteInterface = IPrintSpoolerService.Stub.asInterface(service);
-            } catch (RemoteException re) {
-                /* ignore */
-            }
-        }
-    }
-
-    private IPrintSpoolerService getRemoteInstance(int userId) throws TimeoutException {
-        synchronized (mLock) {
-            if (mRemoteInterface != null && mUserId == userId) {
-                return mRemoteInterface;
-            }
-
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                if (mUserId != UserHandle.USER_NULL && mUserId != userId) {
-                    unbind();
-                }
-
-                mContext.bindServiceAsUser(mIntent, this,
-                        Context.BIND_AUTO_CREATE | Context.BIND_ALLOW_OOM_MANAGEMENT,
-                        UserHandle.CURRENT);
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
-
-            final long startMillis = SystemClock.uptimeMillis();
-            while (true) {
-                if (mRemoteInterface != null) {
-                    break;
-                }
-                final long elapsedMillis = SystemClock.uptimeMillis() - startMillis;
-                final long remainingMillis = BIND_SPOOLER_SERVICE_TIMEOUT - elapsedMillis;
-                if (remainingMillis <= 0) {
-                    throw new TimeoutException("Cannot get spooler!");
-                }
-                try {
-                    mLock.wait(remainingMillis);
-                } catch (InterruptedException ie) {
-                    /* ignore */
-                }
-            }
-
-            mUserId = userId;
-
-            return mRemoteInterface;
-        }
-    }
-
-    public void unbind() {
-        synchronized (mLock) {
-            if (mRemoteInterface != null) {
-                mContext.unbindService(this);
-                mRemoteInterface = null;
-                mUserId = UserHandle.USER_NULL;
-            }
-        }
-    }
-
-    @Override
-    public void binderDied() {
-        synchronized (mLock) {
-            if (mRemoteInterface != null) {
-                mRemoteInterface.asBinder().unlinkToDeath(this, 0);
-                mRemoteInterface = null;
-            }
-        }
-    }
-
-    private final class GetPrintJobsCaller extends TimedRemoteCaller<List<PrintJobInfo>> {
-        private final IPrintSpoolerServiceCallbacks mCallback;
-
-        public GetPrintJobsCaller() {
-            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
-            mCallback = new BasePrintSpoolerServiceCallbacks() {
-                @Override
-                public void onGetPrintJobsResult(List<PrintJobInfo> printJobs, int sequence) {
-                    onRemoteMethodResult(printJobs, sequence);
-                }
-            };
-        }
-
-        public List<PrintJobInfo> getPrintJobs(IPrintSpoolerService target,
-                ComponentName componentName, int state, int appId)
-                        throws RemoteException, TimeoutException {
-            final int sequence = onBeforeRemoteCall();
-            target.getPrintJobs(mCallback, componentName, state, appId, sequence);
-            return getResultTimed(sequence);
-        }
-    }
-
-    private final class CreatePrintJobCaller extends TimedRemoteCaller<PrintJobInfo> {
-        private final IPrintSpoolerServiceCallbacks mCallback;
-
-        public CreatePrintJobCaller() {
-            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
-            mCallback = new BasePrintSpoolerServiceCallbacks() {
-                @Override
-                public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
-                    onRemoteMethodResult(printJob, sequence);
-                }
-            };
-        }
-
-        public PrintJobInfo createPrintJob(IPrintSpoolerService target, String printJobName,
-                IPrintClient client, IPrintAdapter printAdapter, PrintAttributes attributes,
-                int appId) throws RemoteException, TimeoutException {
-            final int sequence = onBeforeRemoteCall();
-            target.createPrintJob(printJobName, client, printAdapter, attributes,
-                    mCallback, appId, sequence);
-            return getResultTimed(sequence);
-        }
-    }
-
-    private final class CancelPrintJobCaller extends TimedRemoteCaller<Boolean> {
-        private final IPrintSpoolerServiceCallbacks 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(IPrintSpoolerService target, int printJobId,
-                int appId) throws RemoteException, TimeoutException {
-            final int sequence = onBeforeRemoteCall();
-            target.cancelPrintJob(printJobId, mCallback, appId, sequence);
-            return getResultTimed(sequence);
-        }
-    }
-
-    private final class GetPrintJobCaller extends TimedRemoteCaller<PrintJobInfo> {
-        private final IPrintSpoolerServiceCallbacks mCallback;
-
-        public GetPrintJobCaller() {
-            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
-            mCallback = new BasePrintSpoolerServiceCallbacks() {
-                @Override
-                public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
-                    onRemoteMethodResult(printJob, sequence);
-                }
-            };
-        }
-
-        public PrintJobInfo getPrintJobInfo(IPrintSpoolerService target, int printJobId,
-                int appId) throws RemoteException, TimeoutException {
-            final int sequence = onBeforeRemoteCall();
-            target.getPrintJob(printJobId, mCallback, appId, sequence);
-            return getResultTimed(sequence);
-        }
-    }
-
-    private final class SetPrintJobStateCaller extends TimedRemoteCaller<Boolean> {
-        private final IPrintSpoolerServiceCallbacks mCallback;
-
-        public SetPrintJobStateCaller() {
-            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
-            mCallback = new BasePrintSpoolerServiceCallbacks() {
-                @Override
-                public void onSetPrintJobStateResult(boolean success, int sequence) {
-                    onRemoteMethodResult(success, sequence);
-                }
-            };
-        }
-
-        public boolean setPrintJobState(IPrintSpoolerService target, int printJobId,
-                int status) throws RemoteException, TimeoutException {
-            final int sequence = onBeforeRemoteCall();
-            target.setPrintJobState(printJobId, status, mCallback, sequence);
-            return getResultTimed(sequence);
-        }
-    }
-
-    private final class SetPrintJobTagCaller extends TimedRemoteCaller<Boolean> {
-        private final IPrintSpoolerServiceCallbacks mCallback;
-
-        public SetPrintJobTagCaller() {
-            super(TimedRemoteCaller.DEFAULT_CALL_TIMEOUT_MILLIS);
-            mCallback = new BasePrintSpoolerServiceCallbacks() {
-                @Override
-                public void onSetPrintJobTagResult(boolean success, int sequence) {
-                    onRemoteMethodResult(success, sequence);
-                }
-            };
-        }
-
-        public boolean setPrintJobTag(IPrintSpoolerService target, int printJobId,
-                String tag) throws RemoteException, TimeoutException {
-            final int sequence = onBeforeRemoteCall();
-            target.setPrintJobTag(printJobId, tag, mCallback, sequence);
-            return getResultTimed(sequence);
-        }
-    }
-
-    private abstract class BasePrintSpoolerServiceCallbacks
-            extends IPrintSpoolerServiceCallbacks.Stub {
-        @Override
-        public void onGetPrintJobsResult(List<PrintJobInfo> printJobIds, int sequence) {
-            /** do nothing */
-        }
-
-        @Override
-        public void onGetPrintJobInfoResult(PrintJobInfo printJob, int sequence) {
-            /** do nothing */
-        }
-
-        @Override
-        public void onCreatePrintJobResult(PrintJobInfo printJob, int sequence) {
-            /** do nothing */
-        }
-
-        @Override
-        public void onCancelPrintJobResult(boolean canceled, int sequence) {
-            /** do nothing */
-        }
-
-        @Override
-        public void onSetPrintJobStateResult(boolean success, int sequece) {
-            /** do nothing */
-        }
-
-        @Override
-        public void onSetPrintJobTagResult(boolean success, int sequence) {
-            /** do nothing */
-        }
-    }
-}
\ No newline at end of file
diff --git a/services/java/com/android/server/print/UserState.java b/services/java/com/android/server/print/UserState.java
new file mode 100644
index 0000000..5cef4d3
--- /dev/null
+++ b/services/java/com/android/server/print/UserState.java
@@ -0,0 +1,274 @@
+/*
+ * 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.server.print;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.print.IPrinterDiscoveryObserver;
+import android.print.PrintJobInfo;
+import android.printservice.PrintServiceInfo;
+import android.provider.Settings;
+import android.text.TextUtils;
+import android.text.TextUtils.SimpleStringSplitter;
+import android.util.Slog;
+
+import com.android.server.print.RemotePrintSpooler.PrintSpoolerCallbacks;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Represents the print state for a user.
+ */
+final class UserState implements PrintSpoolerCallbacks {
+
+    private static final String LOG_TAG = "UserState";
+
+    private static final char COMPONENT_NAME_SEPARATOR = ':';
+
+    private final SimpleStringSplitter mStringColonSplitter =
+            new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR);
+
+    private final Intent mQueryIntent =
+            new Intent(android.printservice.PrintService.SERVICE_INTERFACE);
+
+    private final Map<ComponentName, RemotePrintService> mActiveServices =
+            new HashMap<ComponentName, RemotePrintService>();
+
+    private final List<PrintServiceInfo> mInstalledServices =
+            new ArrayList<PrintServiceInfo>();
+
+    private final Set<ComponentName> mEnabledServices =
+            new HashSet<ComponentName>();
+
+    private final Object mLock;
+
+    private final Context mContext;
+
+    private final int mUserId;
+
+    private final RemotePrintSpooler mSpooler;
+
+    private boolean mDestroyed;
+
+    public UserState(Context context, int userId, Object lock) {
+        mContext = context;
+        mUserId = userId;
+        mLock = lock;
+        mSpooler = new RemotePrintSpooler(context, userId, this);
+    }
+
+    @Override
+    public void onPrintJobQueued(PrintJobInfo printJob) {
+        final RemotePrintService service;
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            ComponentName printServiceName = printJob.getPrinterId().getService();
+            service = mActiveServices.get(printServiceName);
+        }
+        if (service != null) {
+            service.onPrintJobQueued(printJob);
+        }
+    }
+
+    @Override
+    public void onAllPrintJobsForServiceHandled(ComponentName printService) {
+        final RemotePrintService service;
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            service = mActiveServices.get(printService);
+        }
+        if (service != null) {
+            service.onAllPrintJobsHandled();
+        }
+    }
+
+    @Override
+    public void onStartPrinterDiscovery(IPrinterDiscoveryObserver observer) {
+        final List<RemotePrintService> services;
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            if (mActiveServices.isEmpty()) {
+                return;
+            }
+            services = new ArrayList<RemotePrintService>(mActiveServices.values());
+        }
+        final int serviceCount = services.size();
+        for (int i = 0; i < serviceCount; i++) {
+            RemotePrintService service = services.get(i);
+            service.onStartPrinterDiscovery(observer);
+        }
+    }
+
+    @Override
+    public void onStopPrinterDiscovery() {
+        final List<RemotePrintService> services;
+        synchronized (mLock) {
+            throwIfDestroyedLocked();
+            if (mActiveServices.isEmpty()) {
+                return;
+            }
+            services = new ArrayList<RemotePrintService>(mActiveServices.values());
+        }
+        final int serviceCount = services.size();
+        for (int i = 0; i < serviceCount; i++) {
+            RemotePrintService service = services.get(i);
+            service.onStopPrinterDiscovery();
+        }
+    }
+
+    public void updateIfNeededLocked() {
+        throwIfDestroyedLocked();
+        if (readConfigurationLocked()) {
+            onConfigurationChangedLocked();
+        }
+    }
+
+    public RemotePrintSpooler getSpoolerLocked() {
+        throwIfDestroyedLocked();
+        return mSpooler;
+    }
+
+    public Map<ComponentName, RemotePrintService> getActiveServices() {
+        synchronized(mLock) {
+            throwIfDestroyedLocked();
+            return mActiveServices;
+        }
+    }
+
+    public Set<ComponentName> getEnabledServices() {
+        synchronized(mLock) {
+            throwIfDestroyedLocked();
+            return mEnabledServices;
+        }
+    }
+
+    public void destroyLocked() {
+        throwIfDestroyedLocked();
+        mSpooler.destroy();
+        for (RemotePrintService service : mActiveServices.values()) {
+            service.destroy();
+        }
+        mActiveServices.clear();
+        mInstalledServices.clear();
+        mEnabledServices.clear();
+        mDestroyed = true;
+    }
+
+    private boolean readConfigurationLocked() {
+        boolean somethingChanged = false;
+        somethingChanged |= readInstalledPrintServicesLocked();
+        somethingChanged |= readEnabledPrintServicesLocked();
+        return somethingChanged;
+    }
+
+    private boolean readInstalledPrintServicesLocked() {
+        Set<PrintServiceInfo> tempPrintServices = new HashSet<PrintServiceInfo>();
+
+        List<ResolveInfo> installedServices = mContext.getPackageManager()
+                .queryIntentServicesAsUser(mQueryIntent, PackageManager.GET_SERVICES
+                        | PackageManager.GET_META_DATA, mUserId);
+
+        final int installedCount = installedServices.size();
+        for (int i = 0, count = installedCount; i < count; i++) {
+            ResolveInfo installedService = installedServices.get(i);
+            if (!android.Manifest.permission.BIND_PRINT_SERVICE.equals(
+                    installedService.serviceInfo.permission)) {
+                ComponentName serviceName = new ComponentName(
+                        installedService.serviceInfo.packageName,
+                        installedService.serviceInfo.name);
+                Slog.w(LOG_TAG, "Skipping print service "
+                        + serviceName.flattenToShortString()
+                        + " since it does not require permission "
+                        + android.Manifest.permission.BIND_PRINT_SERVICE);
+                continue;
+            }
+            tempPrintServices.add(PrintServiceInfo.create(installedService, mContext));
+        }
+
+        if (!tempPrintServices.equals(mInstalledServices)) {
+            mInstalledServices.clear();
+            mInstalledServices.addAll(tempPrintServices);
+            return true;
+        }
+
+        return false;
+    }
+
+    private boolean readEnabledPrintServicesLocked() {
+        Set<ComponentName> tempEnabledServiceNameSet = new HashSet<ComponentName>();
+
+        String settingValue = Settings.Secure.getStringForUser(mContext.getContentResolver(),
+                Settings.Secure.ENABLED_PRINT_SERVICES, mUserId);
+        if (!TextUtils.isEmpty(settingValue)) {
+            TextUtils.SimpleStringSplitter splitter = mStringColonSplitter;
+            splitter.setString(settingValue);
+            while (splitter.hasNext()) {
+                String string = splitter.next();
+                if (TextUtils.isEmpty(string)) {
+                    continue;
+                }
+                ComponentName componentName = ComponentName.unflattenFromString(string);
+                if (componentName != null) {
+                    tempEnabledServiceNameSet.add(componentName);
+                }
+            }
+        }
+
+        if (!tempEnabledServiceNameSet.equals(mEnabledServices)) {
+            mEnabledServices.clear();
+            mEnabledServices.addAll(tempEnabledServiceNameSet);
+            return true;
+        }
+
+        return false;
+    }
+
+    private void onConfigurationChangedLocked() {
+        final int installedCount = mInstalledServices.size();
+        for (int i = 0; i < installedCount; i++) {
+            ResolveInfo resolveInfo = mInstalledServices.get(i).getResolveInfo();
+            ComponentName serviceName = new ComponentName(resolveInfo.serviceInfo.packageName,
+                    resolveInfo.serviceInfo.name);
+            if (mEnabledServices.contains(serviceName)) {
+                if (!mActiveServices.containsKey(serviceName)) {
+                    mActiveServices.put(serviceName, new RemotePrintService(
+                            mContext, serviceName, mUserId, mSpooler));
+                }
+            } else {
+                RemotePrintService service = mActiveServices.remove(serviceName);
+                if (service != null) {
+                    service.destroy();
+                }
+            }
+        }
+    }
+
+    private void throwIfDestroyedLocked() {
+        if (mDestroyed) {
+            throw new IllegalStateException("Cannot interact with a destroyed instance.");
+        }
+    }
+}
+
diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java
index cacc723..d22178d 100644
--- a/services/java/com/android/server/wm/InputMonitor.java
+++ b/services/java/com/android/server/wm/InputMonitor.java
@@ -19,7 +19,6 @@
 import com.android.server.input.InputManagerService;
 import com.android.server.input.InputApplicationHandle;
 import com.android.server.input.InputWindowHandle;
-import com.android.server.wm.WindowManagerService.AllWindowsIterator;
 
 import android.app.ActivityManagerNative;
 import android.graphics.Rect;
@@ -259,45 +258,47 @@
         }
 
         // Add all windows on the default display.
-        final AllWindowsIterator iterator = mService.new AllWindowsIterator(
-                WindowManagerService.REVERSE_ITERATOR);
-        while (iterator.hasNext()) {
-            final WindowState child = iterator.next();
-            final InputChannel inputChannel = child.mInputChannel;
-            final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
-            if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
-                // Skip this window because it cannot possibly receive input.
-                continue;
-            }
-
-            final int flags = child.mAttrs.flags;
-            final int type = child.mAttrs.type;
-
-            final boolean hasFocus = (child == mInputFocus);
-            final boolean isVisible = child.isVisibleLw();
-            final boolean hasWallpaper = (child == mService.mWallpaperTarget)
-                    && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
-            final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);
-
-            // If there's a drag in progress and 'child' is a potential drop target,
-            // make sure it's been told about the drag
-            if (inDrag && isVisible && onDefaultDisplay) {
-                mService.mDragState.sendDragStartedIfNeededLw(child);
-            }
-
-            if (universeBackground != null && !addedUniverse
-                    && child.mBaseLayer < aboveUniverseLayer && onDefaultDisplay) {
-                final WindowState u = universeBackground.mWin;
-                if (u.mInputChannel != null && u.mInputWindowHandle != null) {
-                    addInputWindowHandleLw(u.mInputWindowHandle, u, u.mAttrs.flags,
-                            u.mAttrs.type, true, u == mInputFocus, false);
+        final int numDisplays = mService.mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            WindowList windows = mService.mDisplayContents.valueAt(displayNdx).getWindowList();
+            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+                final WindowState child = windows.get(winNdx);
+                final InputChannel inputChannel = child.mInputChannel;
+                final InputWindowHandle inputWindowHandle = child.mInputWindowHandle;
+                if (inputChannel == null || inputWindowHandle == null || child.mRemoved) {
+                    // Skip this window because it cannot possibly receive input.
+                    continue;
                 }
-                addedUniverse = true;
-            }
 
-            if (child.mWinAnimator != universeBackground) {
-                addInputWindowHandleLw(inputWindowHandle, child, flags, type,
-                        isVisible, hasFocus, hasWallpaper);
+                final int flags = child.mAttrs.flags;
+                final int type = child.mAttrs.type;
+
+                final boolean hasFocus = (child == mInputFocus);
+                final boolean isVisible = child.isVisibleLw();
+                final boolean hasWallpaper = (child == mService.mWallpaperTarget)
+                        && (type != WindowManager.LayoutParams.TYPE_KEYGUARD);
+                final boolean onDefaultDisplay = (child.getDisplayId() == Display.DEFAULT_DISPLAY);
+
+                // If there's a drag in progress and 'child' is a potential drop target,
+                // make sure it's been told about the drag
+                if (inDrag && isVisible && onDefaultDisplay) {
+                    mService.mDragState.sendDragStartedIfNeededLw(child);
+                }
+
+                if (universeBackground != null && !addedUniverse
+                        && child.mBaseLayer < aboveUniverseLayer && onDefaultDisplay) {
+                    final WindowState u = universeBackground.mWin;
+                    if (u.mInputChannel != null && u.mInputWindowHandle != null) {
+                        addInputWindowHandleLw(u.mInputWindowHandle, u, u.mAttrs.flags,
+                                u.mAttrs.type, true, u == mInputFocus, false);
+                    }
+                    addedUniverse = true;
+                }
+
+                if (child.mWinAnimator != universeBackground) {
+                    addInputWindowHandleLw(inputWindowHandle, child, flags, type,
+                            isVisible, hasFocus, hasWallpaper);
+                }
             }
         }
 
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 3fc3ac6..afdbc87 100644
--- a/services/java/com/android/server/wm/WindowAnimator.java
+++ b/services/java/com/android/server/wm/WindowAnimator.java
@@ -24,7 +24,6 @@
 import android.view.WindowManagerPolicy;
 import android.view.animation.Animation;
 
-import com.android.server.wm.WindowManagerService.DisplayContentsIterator;
 import com.android.server.wm.WindowManagerService.LayoutFields;
 
 import java.io.PrintWriter;
@@ -442,7 +441,7 @@
                             setAppLayoutChanges(appAnimator,
                                     WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM,
                                     "testTokenMayBeDrawnLocked");
-    
+
                             // We can now show all of the drawn windows!
                             if (!mService.mOpeningApps.contains(wtoken)) {
                                 mAnimating |= appAnimator.showAllWindowsLocked();
@@ -541,9 +540,9 @@
         }
 
         boolean hasPendingLayoutChanges = false;
-        DisplayContentsIterator iterator = mService.new DisplayContentsIterator();
-        while (iterator.hasNext()) {
-            final DisplayContent displayContent = iterator.next();
+        final int numDisplays = mService.mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final DisplayContent displayContent = mService.mDisplayContents.get(displayNdx);
             final int pendingChanges = getPendingLayoutChanges(displayContent.getDisplayId());
             if ((pendingChanges & WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
                 mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index 7784b16..3e7509a 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -150,7 +150,6 @@
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.NoSuchElementException;
 
 /** {@hide} */
 public class WindowManagerService extends IWindowManager.Stub
@@ -426,9 +425,7 @@
     String mLastANRState;
 
     /** All DisplayContents in the world, kept here */
-    private SparseArray<DisplayContent> mDisplayContents = new SparseArray<DisplayContent>(2);
-
-    private final AllWindowsIterator mTmpWindowsIterator = new AllWindowsIterator();
+    SparseArray<DisplayContent> mDisplayContents = new SparseArray<DisplayContent>(2);
 
     int mRotation = 0;
     int mForcedAppOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
@@ -2537,13 +2534,17 @@
 
     public void updateAppOpsState() {
         synchronized(mWindowMap) {
-            mTmpWindowsIterator.reset(FORWARD_ITERATOR);
-            while (mTmpWindowsIterator.hasNext()) {
-                final WindowState win = mTmpWindowsIterator.next();
-                if (win.mAppOp != AppOpsManager.OP_NONE) {
-                    final int mode = mAppOps.checkOpNoThrow(win.mAppOp, win.getOwningUid(),
-                            win.getOwningPackage());
-                    win.setAppOpVisibilityLw(mode == AppOpsManager.MODE_ALLOWED);
+            final int numDisplays = mDisplayContents.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList();
+                final int numWindows = windows.size();
+                for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
+                    final WindowState win = windows.get(winNdx);
+                    if (win.mAppOp != AppOpsManager.OP_NONE) {
+                        final int mode = mAppOps.checkOpNoThrow(win.mAppOp, win.getOwningUid(),
+                                win.getOwningPackage());
+                        win.setAppOpVisibilityLw(mode == AppOpsManager.MODE_ALLOWED);
+                    }
                 }
             }
         }
@@ -4551,9 +4552,9 @@
     }
 
     void dumpAppTokensLocked() {
-        DisplayContentsIterator iterator = new DisplayContentsIterator();
-        while (iterator.hasNext()) {
-            DisplayContent displayContent = iterator.next();
+        final int numDisplays = mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
             Slog.v(TAG, "  Display " + displayContent.getDisplayId());
             final ArrayList<Task> tasks = displayContent.getTasks();
             int i = displayContent.numTokens();
@@ -4569,10 +4570,12 @@
 
     void dumpWindowsLocked() {
         int i = 0;
-        mTmpWindowsIterator.reset(REVERSE_ITERATOR);
-        while (mTmpWindowsIterator.hasNext()) {
-            final WindowState w = mTmpWindowsIterator.next();
-            Slog.v(TAG, "  #" + i++ + ": " + w);
+        final int numDisplays = mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList();
+            for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
+                Slog.v(TAG, "  #" + i++ + ": " + windows.get(winNdx));
+            }
         }
     }
 
@@ -4823,9 +4826,9 @@
                         "createStack: weight must be between " + STACK_WEIGHT_MIN + " and " +
                         STACK_WEIGHT_MAX + ", weight=" + weight);
             }
-            DisplayContentsIterator iterator = new DisplayContentsIterator();
-            while (iterator.hasNext()) {
-                final DisplayContent displayContent = iterator.next();
+            final int numDisplays = mDisplayContents.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
                 TaskStack stack = displayContent.createStack(this, stackId, relativeStackBoxId,
                         position, weight);
                 if (stack != null) {
@@ -4888,9 +4891,9 @@
                     STACK_WEIGHT_MAX + ", weight=" + weight);
         }
         synchronized (mWindowMap) {
-            DisplayContentsIterator iterator = new DisplayContentsIterator();
-            while (iterator.hasNext()) {
-                if (iterator.next().resizeStack(stackBoxId, weight)) {
+            final int numDisplays = mDisplayContents.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                if (mDisplayContents.valueAt(displayNdx).resizeStack(stackBoxId, weight)) {
                     performLayoutAndPlaceSurfacesLocked();
                     return;
                 }
@@ -4907,9 +4910,9 @@
     }
 
     public Rect getStackBounds(int stackId) {
-        DisplayContentsIterator iterator = new DisplayContentsIterator();
-        while (iterator.hasNext()) {
-            Rect bounds = iterator.next().getStackBounds(stackId);
+        final int numDisplays = mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            Rect bounds = mDisplayContents.valueAt(displayNdx).getStackBounds(stackId);
             if (bounds != null) {
                 return bounds;
             }
@@ -5036,13 +5039,17 @@
     @Override
     public void closeSystemDialogs(String reason) {
         synchronized(mWindowMap) {
-            mTmpWindowsIterator.reset(FORWARD_ITERATOR);
-            while (mTmpWindowsIterator.hasNext()) {
-                final WindowState w = mTmpWindowsIterator.next();
-                if (w.mHasSurface) {
-                    try {
-                        w.mClient.closeSystemDialogs(reason);
-                    } catch (RemoteException e) {
+            final int numDisplays = mDisplayContents.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList();
+                final int numWindows = windows.size();
+                for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
+                    final WindowState w = windows.get(winNdx);
+                    if (w.mHasSurface) {
+                        try {
+                            w.mClient.closeSystemDialogs(reason);
+                        } catch (RemoteException e) {
+                        }
                     }
                 }
             }
@@ -5180,9 +5187,9 @@
             mPolicy.setCurrentUserLw(newUserId);
 
             // Hide windows that should not be seen by the new user.
-            DisplayContentsIterator iterator = new DisplayContentsIterator();
-            while (iterator.hasNext()) {
-                DisplayContent displayContent = iterator.next();
+            final int numDisplays = mDisplayContents.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
                 displayContent.switchUserStacks(oldUserId, newUserId);
                 rebuildAppWindowListLocked(displayContent);
             }
@@ -5432,12 +5439,16 @@
             // the background..)
             if (on) {
                 boolean isVisible = false;
-                mTmpWindowsIterator.reset(FORWARD_ITERATOR);
-                while (mTmpWindowsIterator.hasNext()) {
-                    final WindowState ws = mTmpWindowsIterator.next();
-                    if (ws.mSession.mPid == pid && ws.isVisibleLw()) {
-                        isVisible = true;
-                        break;
+                final int numDisplays = mDisplayContents.size();
+                for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                    final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList();
+                    final int numWindows = windows.size();
+                    for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
+                        final WindowState ws = windows.get(winNdx);
+                        if (ws.mSession.mPid == pid && ws.isVisibleLw()) {
+                            isVisible = true;
+                            break;
+                        }
                     }
                 }
                 if (!isVisible) {
@@ -6197,9 +6208,10 @@
         WindowList windows = new WindowList();
         synchronized (mWindowMap) {
             //noinspection unchecked
-            DisplayContentsIterator iterator = new DisplayContentsIterator();
-            while(iterator.hasNext()) {
-                windows.addAll(iterator.next().getWindowList());
+            final int numDisplays = mDisplayContents.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
+                windows.addAll(displayContent.getWindowList());
             }
         }
 
@@ -6427,11 +6439,15 @@
         }
 
         synchronized (mWindowMap) {
-            mTmpWindowsIterator.reset(FORWARD_ITERATOR);
-            while (mTmpWindowsIterator.hasNext()) {
-                final WindowState w = mTmpWindowsIterator.next();
-                if (System.identityHashCode(w) == hashCode) {
-                    return w;
+            final int numDisplays = mDisplayContents.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList();
+                final int numWindows = windows.size();
+                for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
+                    final WindowState w = windows.get(winNdx);
+                    if (System.identityHashCode(w) == hashCode) {
+                        return w;
+                    }
                 }
             }
         }
@@ -6982,12 +6998,16 @@
     // TODO(multidisplay): Call isScreenOn for each display.
     private void sendScreenStatusToClientsLocked() {
         final boolean on = mPowerManager.isScreenOn();
-        mTmpWindowsIterator.reset(FORWARD_ITERATOR);
-        while (mTmpWindowsIterator.hasNext()) {
-            try {
-                mTmpWindowsIterator.next().mClient.dispatchScreenState(on);
-            } catch (RemoteException e) {
-                // Ignored
+        final int numDisplays = mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList();
+            final int numWindows = windows.size();
+            for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
+                try {
+                    windows.get(winNdx).mClient.dispatchScreenState(on);
+                } catch (RemoteException e) {
+                    // Ignored
+                }
             }
         }
     }
@@ -7849,9 +7869,10 @@
     }
 
     final void rebuildAppWindowListLocked() {
-        DisplayContentsIterator iterator = new DisplayContentsIterator();
-        while (iterator.hasNext()) {
-            rebuildAppWindowListLocked(iterator.next());
+        final int numDisplays = mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
+            rebuildAppWindowListLocked(displayContent);
         }
     }
 
@@ -8800,9 +8821,9 @@
         }
 
         // Initialize state of exiting tokens.
-        DisplayContentsIterator iterator = new DisplayContentsIterator();
-        while (iterator.hasNext()) {
-            final DisplayContent displayContent = iterator.next();
+        final int numDisplays = mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
             for (i=displayContent.mExitingTokens.size()-1; i>=0; i--) {
                 displayContent.mExitingTokens.get(i).hasVisible = false;
             }
@@ -8840,10 +8861,9 @@
 
             boolean focusDisplayed = false;
 
-            iterator = new DisplayContentsIterator();
-            while (iterator.hasNext()) {
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
                 boolean updateAllDrawn = false;
-                final DisplayContent displayContent = iterator.next();
                 WindowList windows = displayContent.getWindowList();
                 DisplayInfo displayInfo = displayContent.getDisplayInfo();
                 final int displayId = displayContent.getDisplayId();
@@ -9255,9 +9275,8 @@
         }
 
         // Time to remove any exiting tokens?
-        iterator = new DisplayContentsIterator();
-        while (iterator.hasNext()) {
-            final DisplayContent displayContent = iterator.next();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
             ArrayList<WindowToken> exitingTokens = displayContent.mExitingTokens;
             for (i = exitingTokens.size() - 1; i >= 0; i--) {
                 WindowToken token = exitingTokens.get(i);
@@ -9306,9 +9325,8 @@
             defaultDisplay.layoutNeeded = true;
         }
 
-        iterator = new DisplayContentsIterator();
-        while (iterator.hasNext()) {
-            DisplayContent displayContent = iterator.next();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
             if (displayContent.pendingLayoutChanges != 0) {
                 displayContent.layoutNeeded = true;
             }
@@ -9489,9 +9507,10 @@
     }
 
     private boolean needsLayout() {
-        DisplayContentsIterator iterator = new DisplayContentsIterator();
-        while (iterator.hasNext()) {
-            if (iterator.next().layoutNeeded) {
+        final int numDisplays = mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
+            if (displayContent.layoutNeeded) {
                 return true;
             }
         }
@@ -9570,35 +9589,39 @@
             // window list to make sure we haven't left any dangling surfaces
             // around.
 
-            mTmpWindowsIterator.reset(FORWARD_ITERATOR);
             Slog.i(TAG, "Out of memory for surface!  Looking for leaks...");
-            while (mTmpWindowsIterator.hasNext()) {
-                WindowState ws = mTmpWindowsIterator.next();
-                WindowStateAnimator wsa = ws.mWinAnimator;
-                if (wsa.mSurfaceControl != null) {
-                    if (!mSessions.contains(wsa.mSession)) {
-                        Slog.w(TAG, "LEAKED SURFACE (session doesn't exist): "
-                                + ws + " surface=" + wsa.mSurfaceControl
-                                + " token=" + ws.mToken
-                                + " pid=" + ws.mSession.mPid
-                                + " uid=" + ws.mSession.mUid);
-                        if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
-                        wsa.mSurfaceControl.destroy();
-                        wsa.mSurfaceShown = false;
-                        wsa.mSurfaceControl = null;
-                        ws.mHasSurface = false;
-                        mForceRemoves.add(ws);
-                        leakedSurface = true;
-                    } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
-                        Slog.w(TAG, "LEAKED SURFACE (app token hidden): "
-                                + ws + " surface=" + wsa.mSurfaceControl
-                                + " token=" + ws.mAppToken);
-                        if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
-                        wsa.mSurfaceControl.destroy();
-                        wsa.mSurfaceShown = false;
-                        wsa.mSurfaceControl = null;
-                        ws.mHasSurface = false;
-                        leakedSurface = true;
+            final int numDisplays = mDisplayContents.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList();
+                final int numWindows = windows.size();
+                for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
+                    final WindowState ws = windows.get(winNdx);
+                    WindowStateAnimator wsa = ws.mWinAnimator;
+                    if (wsa.mSurfaceControl != null) {
+                        if (!mSessions.contains(wsa.mSession)) {
+                            Slog.w(TAG, "LEAKED SURFACE (session doesn't exist): "
+                                    + ws + " surface=" + wsa.mSurfaceControl
+                                    + " token=" + ws.mToken
+                                    + " pid=" + ws.mSession.mPid
+                                    + " uid=" + ws.mSession.mUid);
+                            if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
+                            wsa.mSurfaceControl.destroy();
+                            wsa.mSurfaceShown = false;
+                            wsa.mSurfaceControl = null;
+                            ws.mHasSurface = false;
+                            mForceRemoves.add(ws);
+                            leakedSurface = true;
+                        } else if (ws.mAppToken != null && ws.mAppToken.clientHidden) {
+                            Slog.w(TAG, "LEAKED SURFACE (app token hidden): "
+                                    + ws + " surface=" + wsa.mSurfaceControl
+                                    + " token=" + ws.mAppToken);
+                            if (SHOW_TRANSACTIONS) logSurface(ws, "LEAK DESTROY", null);
+                            wsa.mSurfaceControl.destroy();
+                            wsa.mSurfaceShown = false;
+                            wsa.mSurfaceControl = null;
+                            ws.mHasSurface = false;
+                            leakedSurface = true;
+                        }
                     }
                 }
             }
@@ -9606,27 +9629,30 @@
             if (!leakedSurface) {
                 Slog.w(TAG, "No leaked surfaces; killing applicatons!");
                 SparseIntArray pidCandidates = new SparseIntArray();
-                mTmpWindowsIterator.reset(FORWARD_ITERATOR);
-                while (mTmpWindowsIterator.hasNext()) {
-                    WindowState ws = mTmpWindowsIterator.next();
-                    if (mForceRemoves.contains(ws)) {
-                        continue;
-                    }
-                    WindowStateAnimator wsa = ws.mWinAnimator;
-                    if (wsa.mSurfaceControl != null) {
-                        pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid);
-                    }
-                }
-                if (pidCandidates.size() > 0) {
-                    int[] pids = new int[pidCandidates.size()];
-                    for (int i=0; i<pids.length; i++) {
-                        pids[i] = pidCandidates.keyAt(i);
-                    }
-                    try {
-                        if (mActivityManager.killPids(pids, "Free memory", secure)) {
-                            killedApps = true;
+                for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                    final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList();
+                    final int numWindows = windows.size();
+                    for (int winNdx = 0; winNdx < numWindows; ++winNdx) {
+                        final WindowState ws = windows.get(winNdx);
+                        if (mForceRemoves.contains(ws)) {
+                            continue;
                         }
-                    } catch (RemoteException e) {
+                        WindowStateAnimator wsa = ws.mWinAnimator;
+                        if (wsa.mSurfaceControl != null) {
+                            pidCandidates.append(wsa.mSession.mPid, wsa.mSession.mPid);
+                        }
+                    }
+                    if (pidCandidates.size() > 0) {
+                        int[] pids = new int[pidCandidates.size()];
+                        for (int i=0; i<pids.length; i++) {
+                            pids[i] = pidCandidates.keyAt(i);
+                        }
+                        try {
+                            if (mActivityManager.killPids(pids, "Free memory", secure)) {
+                                killedApps = true;
+                            }
+                        } catch (RemoteException e) {
+                        }
                     }
                 }
             }
@@ -10225,9 +10251,10 @@
     void dumpDisplayContentsLocked(PrintWriter pw, boolean dumpAll) {
         pw.println("WINDOW MANAGER DISPLAY CONTENTS (dumpsys window displays)");
         if (mDisplayReady) {
-            DisplayContentsIterator dCIterator = new DisplayContentsIterator();
-            while (dCIterator.hasNext()) {
-                dCIterator.next().dump("  ", pw);
+            final int numDisplays = mDisplayContents.size();
+            for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
+                displayContent.dump("  ", pw);
             }
         } else {
             pw.println("  NO DISPLAY");
@@ -10243,13 +10270,16 @@
     void dumpWindowsNoHeaderLocked(PrintWriter pw, boolean dumpAll,
             ArrayList<WindowState> windows) {
         int j = 0;
-        mTmpWindowsIterator.reset(REVERSE_ITERATOR);
-        while (mTmpWindowsIterator.hasNext()) {
-            final WindowState w = mTmpWindowsIterator.next();
-            if (windows == null || windows.contains(w)) {
-                pw.print("  Window #"); pw.print(j++); pw.print(' ');
-                        pw.print(w); pw.println(":");
-                w.dump(pw, "    ", dumpAll || windows != null);
+        final int numDisplays = mDisplayContents.size();
+        for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+            final WindowList windowList = mDisplayContents.valueAt(displayNdx).getWindowList();
+            for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
+                final WindowState w = windowList.get(winNdx);
+                if (windows == null || windows.contains(w)) {
+                    pw.print("  Window #"); pw.print(j++); pw.print(' ');
+                            pw.print(w); pw.println(":");
+                    w.dump(pw, "    ", dumpAll || windows != null);
+                }
             }
         }
         if (mInputMethodDialogs.size() > 0) {
@@ -10402,9 +10432,8 @@
                     pw.print(" mDisplayEnabled="); pw.println(mDisplayEnabled);
             if (needsLayout()) {
                 pw.print("  layoutNeeded on displays=");
-                DisplayContentsIterator dcIterator = new DisplayContentsIterator();
-                while (dcIterator.hasNext()) {
-                    final DisplayContent displayContent = dcIterator.next();
+                for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                    final DisplayContent displayContent = mDisplayContents.valueAt(displayNdx);
                     if (displayContent.layoutNeeded) {
                         pw.print(displayContent.getDisplayId());
                     }
@@ -10438,11 +10467,15 @@
         WindowList windows = new WindowList();
         if ("visible".equals(name)) {
             synchronized(mWindowMap) {
-                mTmpWindowsIterator.reset(REVERSE_ITERATOR);
-                while (mTmpWindowsIterator.hasNext()) {
-                    final WindowState w = mTmpWindowsIterator.next();
-                    if (w.mWinAnimator.mSurfaceShown) {
-                        windows.add(w);
+                final int numDisplays = mDisplayContents.size();
+                for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                    final WindowList windowList =
+                            mDisplayContents.valueAt(displayNdx).getWindowList();
+                    for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
+                        final WindowState w = windowList.get(winNdx);
+                        if (w.mWinAnimator.mSurfaceShown) {
+                            windows.add(w);
+                        }
                     }
                 }
             }
@@ -10455,15 +10488,19 @@
             } catch (RuntimeException e) {
             }
             synchronized(mWindowMap) {
-                mTmpWindowsIterator.reset(REVERSE_ITERATOR);
-                while (mTmpWindowsIterator.hasNext()) {
-                    final WindowState w = mTmpWindowsIterator.next();
-                    if (name != null) {
-                        if (w.mAttrs.getTitle().toString().contains(name)) {
+                final int numDisplays = mDisplayContents.size();
+                for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {
+                    final WindowList windowList =
+                            mDisplayContents.valueAt(displayNdx).getWindowList();
+                    for (int winNdx = windowList.size() - 1; winNdx >= 0; --winNdx) {
+                        final WindowState w = windowList.get(winNdx);
+                        if (name != null) {
+                            if (w.mAttrs.getTitle().toString().contains(name)) {
+                                windows.add(w);
+                            }
+                        } else if (System.identityHashCode(w) == objectId) {
                             windows.add(w);
                         }
-                    } else if (System.identityHashCode(w) == objectId) {
-                        windows.add(w);
                     }
                 }
             }
@@ -10715,104 +10752,6 @@
         return displayContent;
     }
 
-    class DisplayContentsIterator implements Iterator<DisplayContent> {
-        private int cur;
-
-        void reset() {
-            cur = 0;
-        }
-
-        @Override
-        public boolean hasNext() {
-            return cur < mDisplayContents.size();
-        }
-
-        @Override
-        public DisplayContent next() {
-            if (hasNext()) {
-                return mDisplayContents.valueAt(cur++);
-            }
-            throw new NoSuchElementException();
-        }
-
-        @Override
-        public void remove() {
-            throw new IllegalArgumentException("AllDisplayContentIterator.remove not implemented");
-        }
-    }
-
-    class AllWindowsIterator implements Iterator<WindowState> {
-        private DisplayContent mDisplayContent;
-        private DisplayContentsIterator mDisplayContentsIterator;
-        private WindowList mWindowList;
-        private int mWindowListIndex;
-        private boolean mReverse;
-
-        AllWindowsIterator() {
-            this(false);
-        }
-
-        AllWindowsIterator(boolean reverse) {
-            mDisplayContentsIterator = new DisplayContentsIterator();
-            reset(reverse);
-        }
-
-        void reset(boolean reverse) {
-            mReverse = reverse;
-            mDisplayContentsIterator.reset();
-            if (mDisplayContentsIterator.hasNext()) {
-                mDisplayContent = mDisplayContentsIterator.next();
-                mWindowList = mDisplayContent.getWindowList();
-                mWindowListIndex = reverse ? mWindowList.size() - 1 : 0;
-            } else {
-                mDisplayContent = null;
-                mWindowList = null;
-                mWindowListIndex = 0;
-            }
-        }
-
-        @Override
-        public boolean hasNext() {
-            if (mDisplayContent == null) {
-                return false;
-            }
-            if (mReverse) {
-                return mWindowListIndex >= 0;
-            }
-            return mWindowListIndex < mWindowList.size();
-        }
-
-        @Override
-        public WindowState next() {
-            if (hasNext()) {
-                WindowState win = mWindowList.get(mWindowListIndex);
-                if (mReverse) {
-                    mWindowListIndex--;
-                    if (mWindowListIndex < 0 && mDisplayContentsIterator.hasNext()) {
-                        mDisplayContent = mDisplayContentsIterator.next();
-                        mWindowList = mDisplayContent.getWindowList();
-                        mWindowListIndex = mWindowList.size() - 1;
-                    }
-                } else {
-                    mWindowListIndex++;
-                    if (mWindowListIndex >= mWindowList.size()
-                            && mDisplayContentsIterator.hasNext()) {
-                        mDisplayContent = mDisplayContentsIterator.next();
-                        mWindowList = mDisplayContent.getWindowList();
-                        mWindowListIndex = 0;
-                    }
-                }
-                return win;
-            }
-            throw new NoSuchElementException();
-        }
-
-        @Override
-        public void remove() {
-            throw new IllegalArgumentException("AllWindowsIterator.remove not implemented");
-        }
-    }
-
     // There is an inherent assumption that this will never return null.
     public DisplayContent getDefaultDisplayContentLocked() {
         return getDisplayContentLocked(Display.DEFAULT_DISPLAY);
diff --git a/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java b/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
index 17a1585..192c50c 100644
--- a/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/CountryDetectorServiceTest.java
@@ -66,7 +66,7 @@
 
     public void testAddRemoveListener() throws RemoteException {
         CountryDetectorServiceTester serviceTester = new CountryDetectorServiceTester(getContext());
-        serviceTester.systemReady();
+        serviceTester.systemRunning();
         waitForSystemReady(serviceTester);
         CountryListenerTester listenerTester = new CountryListenerTester();
         serviceTester.addCountryListener(listenerTester);
@@ -80,7 +80,7 @@
         CountryListenerTester listenerTesterA = new CountryListenerTester();
         CountryListenerTester listenerTesterB = new CountryListenerTester();
         Country country = new Country("US", Country.COUNTRY_SOURCE_NETWORK);
-        serviceTester.systemReady();
+        serviceTester.systemRunning();
         waitForSystemReady(serviceTester);
         serviceTester.addCountryListener(listenerTesterA);
         serviceTester.addCountryListener(listenerTesterB);
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 7ee7085..b1dd2ce 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -371,10 +371,12 @@
     }
 
     public void startTdls(String macAddr, boolean enable) {
-        if (enable)
+        if (enable) {
+            doBooleanCommand("TDLS_DISCOVER " + macAddr);
             doBooleanCommand("TDLS_SETUP " + macAddr);
-        else
+        } else {
             doBooleanCommand("TDLS_TEARDOWN " + macAddr);
+        }
     }
 
     /** Example output: