Merge "First shot at (host-based) card emulation APIs."
diff --git a/Android.mk b/Android.mk
index db6a704..98bd88d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -26,7 +26,10 @@
 # TODO: find a more appropriate way to do this.
 framework_res_source_path := APPS/framework-res_intermediates/src
 
-# the library
+# Build the master framework library.
+# The framework contains too many method references (>64K) for poor old DEX.
+# So we first build the framework as a monolithic static library then split it
+# up into smaller pieces.
 # ============================================================
 include $(CLEAR_VARS)
 
@@ -39,14 +42,6 @@
        core/java/android/speech/tts/EventLogTags.logtags \
        core/java/android/webkit/EventLogTags.logtags \
 
-# The following filters out code we are temporarily not including at all.
-# TODO: Move AWT and beans (and associated harmony code) back into libcore.
-# TODO: Maybe remove javax.microedition entirely?
-# TODO: Move SyncML (org.mobilecontrol.*) into its own library.
-LOCAL_SRC_FILES := $(filter-out \
-			org/mobilecontrol/% \
-			,$(LOCAL_SRC_FILES))
-
 ## READ ME: ########################################################
 ##
 ## When updating this list of aidl files, consider if that aidl is
@@ -159,13 +154,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 \
@@ -256,8 +253,6 @@
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
 	wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl
-#
-
 
 # FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk
 LOCAL_AIDL_INCLUDES += $(FRAMEWORKS_BASE_JAVA_SRC_DIRS)
@@ -270,17 +265,11 @@
 LOCAL_NO_STANDARD_LIBRARIES := true
 LOCAL_JAVA_LIBRARIES := bouncycastle conscrypt core core-junit ext okhttp
 
-LOCAL_MODULE := framework
-LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_MODULE := framework-base
 
-# List of classes and interfaces which should be loaded by the Zygote.
-LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
+LOCAL_JAR_EXCLUDE_FILES := none
 
-#LOCAL_JARJAR_RULES := $(LOCAL_PATH)/jarjar-rules.txt
-
-LOCAL_DX_FLAGS := --core-library
-
-include $(BUILD_JAVA_LIBRARY)
+include $(BUILD_STATIC_JAVA_LIBRARY)
 
 # Make sure that R.java and Manifest.java are built before we build
 # the source for this library.
@@ -288,14 +277,53 @@
 	$(call intermediates-dir-for,APPS,framework-res,,COMMON)/src/R.stamp
 $(full_classes_compiled_jar): $(framework_res_R_stamp)
 
-# Make sure that framework-res is installed when framework is.
-$(LOCAL_INSTALLED_MODULE): | $(dir $(LOCAL_INSTALLED_MODULE))framework-res.apk
-
-framework_built := $(call java-lib-deps,framework)
-
-# AIDL files to be preprocessed and included in the SDK,
-# relative to the root of the build tree.
+# Build part 1 of the framework library.
 # ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := framework
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_STATIC_JAVA_LIBRARIES := framework-base
+LOCAL_DX_FLAGS := --core-library
+
+# Packages to include, use \* wildcard to include descendants.
+LOCAL_JAR_PACKAGES := android\*
+
+# List of classes and interfaces which should be loaded by the Zygote.
+LOCAL_JAVA_RESOURCE_FILES += $(LOCAL_PATH)/preloaded-classes
+
+include $(BUILD_JAVA_LIBRARY)
+framework_module := $(LOCAL_INSTALLED_MODULE)
+
+# Build part 2 of the framework library.
+# ============================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := framework2
+LOCAL_MODULE_CLASS := JAVA_LIBRARIES
+LOCAL_NO_STANDARD_LIBRARIES := true
+LOCAL_STATIC_JAVA_LIBRARIES := framework-base
+LOCAL_DX_FLAGS := --core-library
+
+# Packages to include, use \* wildcard to include descendants.
+LOCAL_JAR_PACKAGES := com\* javax\*
+
+include $(BUILD_JAVA_LIBRARY)
+framework2_module := $(LOCAL_INSTALLED_MODULE)
+
+# Make sure that all framework modules are installed when framework is.
+# ============================================================
+$(framework_module): | $(dir $(framework_module))framework-res.apk
+$(framework_module): | $(dir $(framework_module))framework2.jar
+
+framework_built := $(call java-lib-deps,framework framework2)
+
+# Copy AIDL files to be preprocessed and included in the SDK,
+# specified relative to the root of the build tree.
+# ============================================================
+include $(CLEAR_VARS)
+
 aidl_files := \
 	frameworks/base/core/java/android/accounts/IAccountManager.aidl \
 	frameworks/base/core/java/android/accounts/IAccountManagerResponse.aidl \
@@ -441,6 +469,7 @@
 	okhttp \
 	ext \
 	framework \
+	framework2 \
 	mms-common \
 	telephony-common \
 	voip-common
@@ -477,7 +506,7 @@
 		-overview $(LOCAL_PATH)/core/java/overview.html
 
 framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
-	$(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
+	$(call intermediates-dir-for,JAVA_LIBRARIES,framework-base,,COMMON)
 
 framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
 	$(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR) \
@@ -759,7 +788,7 @@
 
 LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES)
 LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) framework
+LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
 LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
 LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
 LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
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 b707197..080c6f9 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -9,6 +9,7 @@
     field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES";
     field public static final java.lang.String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION";
     field public static final java.lang.String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION";
+    field public static final java.lang.String ACCESS_INPUT_FLINGER = "android.permission.ACCESS_INPUT_FLINGER";
     field public static final java.lang.String ACCESS_LOCATION_EXTRA_COMMANDS = "android.permission.ACCESS_LOCATION_EXTRA_COMMANDS";
     field public static final java.lang.String ACCESS_MOCK_LOCATION = "android.permission.ACCESS_MOCK_LOCATION";
     field public static final java.lang.String ACCESS_NETWORK_STATE = "android.permission.ACCESS_NETWORK_STATE";
@@ -2699,7 +2700,8 @@
     method public void addContentView(android.view.View, android.view.ViewGroup.LayoutParams);
     method public void closeContextMenu();
     method public void closeOptionsMenu();
-    method public void convertToOpaque();
+    method public void convertFromTranslucent();
+    method public void convertToTranslucent(android.app.Activity.TranslucentConversionListener);
     method public android.app.PendingIntent createPendingResult(int, android.content.Intent, int);
     method public final deprecated void dismissDialog(int);
     method public boolean dispatchGenericMotionEvent(android.view.MotionEvent);
@@ -2885,6 +2887,10 @@
     field public static final int RESULT_OK = -1; // 0xffffffff
   }
 
+  public static abstract interface Activity.TranslucentConversionListener {
+    method public abstract void onTranslucentConversionComplete(boolean);
+  }
+
   public deprecated class ActivityGroup extends android.app.Activity {
     ctor public ActivityGroup();
     ctor public ActivityGroup(boolean);
@@ -3048,17 +3054,19 @@
   public class AlarmManager {
     method public void cancel(android.app.PendingIntent);
     method public void set(int, long, android.app.PendingIntent);
-    method public void setInexactRepeating(int, long, long, android.app.PendingIntent);
+    method public void setExact(int, long, android.app.PendingIntent);
+    method public deprecated void setInexactRepeating(int, long, long, android.app.PendingIntent);
     method public void setRepeating(int, long, long, android.app.PendingIntent);
     method public void setTime(long);
     method public void setTimeZone(java.lang.String);
+    method public void setWindow(int, long, long, android.app.PendingIntent);
     field public static final int ELAPSED_REALTIME = 3; // 0x3
     field public static final int ELAPSED_REALTIME_WAKEUP = 2; // 0x2
-    field public static final long INTERVAL_DAY = 86400000L; // 0x5265c00L
-    field public static final long INTERVAL_FIFTEEN_MINUTES = 900000L; // 0xdbba0L
-    field public static final long INTERVAL_HALF_DAY = 43200000L; // 0x2932e00L
-    field public static final long INTERVAL_HALF_HOUR = 1800000L; // 0x1b7740L
-    field public static final long INTERVAL_HOUR = 3600000L; // 0x36ee80L
+    field public static final deprecated long INTERVAL_DAY = 86400000L; // 0x5265c00L
+    field public static final deprecated long INTERVAL_FIFTEEN_MINUTES = 900000L; // 0xdbba0L
+    field public static final deprecated long INTERVAL_HALF_DAY = 43200000L; // 0x2932e00L
+    field public static final deprecated long INTERVAL_HALF_HOUR = 1800000L; // 0x1b7740L
+    field public static final deprecated long INTERVAL_HOUR = 3600000L; // 0x36ee80L
     field public static final int RTC = 1; // 0x1
     field public static final int RTC_WAKEUP = 0; // 0x0
   }
@@ -3136,6 +3144,32 @@
     ctor public AliasActivity();
   }
 
+  public class AppOpsManager {
+    method public int checkOp(int, int, java.lang.String);
+    method public int checkOpNoThrow(int, int, java.lang.String);
+    method public void finishOp(int, int, java.lang.String);
+    method public void finishOp(int);
+    method public int noteOp(int, int, java.lang.String);
+    method public int noteOpNoThrow(int, int, java.lang.String);
+    method public static java.lang.String opToName(int);
+    method public int startOp(int, int, java.lang.String);
+    method public int startOpNoThrow(int, int, java.lang.String);
+    method public void startWatchingMode(int, java.lang.String, android.app.AppOpsManager.Callback);
+    method public void stopWatchingMode(android.app.AppOpsManager.Callback);
+    field public static final int MODE_ALLOWED = 0; // 0x0
+    field public static final int MODE_ERRORED = 2; // 0x2
+    field public static final int MODE_IGNORED = 1; // 0x1
+    field public static final int OP_COARSE_LOCATION = 0; // 0x0
+    field public static final int OP_FINE_LOCATION = 1; // 0x1
+    field public static final int OP_GPS = 2; // 0x2
+    field public static final int OP_MONITOR_LOCATION = 41; // 0x29
+    field public static final int OP_NONE = -1; // 0xffffffff
+  }
+
+  public static abstract interface AppOpsManager.Callback {
+    method public abstract void opChanged(int, java.lang.String);
+  }
+
   public class Application extends android.content.ContextWrapper implements android.content.ComponentCallbacks2 {
     ctor public Application();
     method public void onConfigurationChanged(android.content.res.Configuration);
@@ -7084,6 +7118,7 @@
     field public static final java.lang.String FEATURE_LOCATION_NETWORK = "android.hardware.location.network";
     field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone";
     field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc";
+    field public static final java.lang.String FEATURE_NFC_HCE = "android.hardware.nfc.hce";
     field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape";
     field public static final java.lang.String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait";
     field public static final java.lang.String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer";
@@ -8712,13 +8747,17 @@
     method public final boolean isPremultiplied();
     method public final boolean isRecycled();
     method public void prepareToDraw();
+    method public void reconfigure(int, int, android.graphics.Bitmap.Config);
     method public void recycle();
     method public boolean sameAs(android.graphics.Bitmap);
+    method public void setConfig(android.graphics.Bitmap.Config);
     method public void setDensity(int);
     method public void setHasAlpha(boolean);
     method public final void setHasMipMap(boolean);
+    method public void setHeight(int);
     method public void setPixel(int, int, int);
     method public void setPixels(int[], int, int, int, int, int, int);
+    method public void setWidth(int);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int DENSITY_NONE = 0; // 0x0
@@ -10731,7 +10770,7 @@
   }
 
   public static class CameraMetadata.Key {
-    ctor public CameraMetadata.Key(java.lang.String);
+    ctor public CameraMetadata.Key(java.lang.String, java.lang.Class<T>);
     method public final boolean equals(java.lang.Object);
     method public final java.lang.String getName();
     method public final int hashCode();
@@ -10783,7 +10822,14 @@
     method public int getScore();
   }
 
+  public final class Rational {
+    ctor public Rational(int, int);
+    method public int getDenominator();
+    method public int getNumerator();
+  }
+
   public final class Size {
+    ctor public Size(int, int);
     method public final int getHeight();
     method public final int getWidth();
   }
@@ -11572,6 +11618,7 @@
     field public static final int FX_FOCUS_NAVIGATION_RIGHT = 4; // 0x4
     field public static final int FX_FOCUS_NAVIGATION_UP = 1; // 0x1
     field public static final int FX_KEYPRESS_DELETE = 7; // 0x7
+    field public static final int FX_KEYPRESS_INVALID = 9; // 0x9
     field public static final int FX_KEYPRESS_RETURN = 8; // 0x8
     field public static final int FX_KEYPRESS_SPACEBAR = 6; // 0x6
     field public static final int FX_KEYPRESS_STANDARD = 5; // 0x5
@@ -13425,6 +13472,7 @@
 
   public class LocalSocket implements java.io.Closeable {
     ctor public LocalSocket();
+    ctor public LocalSocket(int);
     method public void bind(android.net.LocalSocketAddress) throws java.io.IOException;
     method public void close() throws java.io.IOException;
     method public void connect(android.net.LocalSocketAddress) throws java.io.IOException;
@@ -13450,6 +13498,9 @@
     method public void setSoTimeout(int) throws java.io.IOException;
     method public void shutdownInput() throws java.io.IOException;
     method public void shutdownOutput() throws java.io.IOException;
+    field public static final int SOCKET_DGRAM = 1; // 0x1
+    field public static final int SOCKET_SEQPACKET = 3; // 0x3
+    field public static final int SOCKET_STREAM = 2; // 0x2
   }
 
   public class LocalSocketAddress {
@@ -18436,36 +18487,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();
@@ -18516,64 +18537,103 @@
   }
 
   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);
+    method public java.lang.CharSequence getLabel();
     method public int getVerticalDpi();
   }
 
   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);
+    method public java.lang.CharSequence getLabel();
+  }
+
+  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, android.os.Bundle);
+    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);
+    field public static final java.lang.String METADATA_KEY_PRINT_PREVIEW = "KEY_METADATA_PRINT_PREVIEW";
+  }
+
+  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 {
@@ -18596,7 +18656,6 @@
     field public static final int PRINT_JOB_ID_UNDEFINED = -1; // 0xffffffff
     field public static final int STATE_CANCELED = 6; // 0x6
     field public static final int STATE_COMPLETED = 4; // 0x4
-    field public static final int STATE_CREATED = 1; // 0x1
     field public static final int STATE_FAILED = 5; // 0x5
     field public static final int STATE_QUEUED = 2; // 0x2
     field public static final int STATE_STARTED = 3; // 0x3
@@ -18605,7 +18664,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 {
@@ -18687,11 +18746,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();
@@ -24769,6 +24833,33 @@
     ctor public AndroidRuntimeException(java.lang.Exception);
   }
 
+  public final class ArrayMap implements java.util.Map {
+    ctor public ArrayMap();
+    ctor public ArrayMap(int);
+    ctor public ArrayMap(android.util.ArrayMap);
+    method public void clear();
+    method public boolean containsAll(java.util.Collection<?>);
+    method public boolean containsKey(java.lang.Object);
+    method public boolean containsValue(java.lang.Object);
+    method public void ensureCapacity(int);
+    method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
+    method public V get(java.lang.Object);
+    method public boolean isEmpty();
+    method public K keyAt(int);
+    method public java.util.Set<K> keySet();
+    method public V put(K, V);
+    method public void putAll(android.util.ArrayMap<? extends K, ? extends V>);
+    method public void putAll(java.util.Map<? extends K, ? extends V>);
+    method public V remove(java.lang.Object);
+    method public boolean removeAll(java.util.Collection<?>);
+    method public V removeAt(int);
+    method public boolean retainAll(java.util.Collection<?>);
+    method public V setValueAt(int, V);
+    method public int size();
+    method public V valueAt(int);
+    method public java.util.Collection<V> values();
+  }
+
   public class AtomicFile {
     ctor public AtomicFile(java.io.File);
     method public void delete();
@@ -25122,6 +25213,7 @@
     method public void put(int, E);
     method public void remove(int);
     method public void removeAt(int);
+    method public void removeAtRange(int, int);
     method public void setValueAt(int, E);
     method public int size();
     method public E valueAt(int);
@@ -25860,6 +25952,7 @@
     field public static final int KEYCODE_LEFT_BRACKET = 71; // 0x47
     field public static final int KEYCODE_M = 41; // 0x29
     field public static final int KEYCODE_MANNER_MODE = 205; // 0xcd
+    field public static final int KEYCODE_MEDIA_AUDIO_TRACK = 222; // 0xde
     field public static final int KEYCODE_MEDIA_CLOSE = 128; // 0x80
     field public static final int KEYCODE_MEDIA_EJECT = 129; // 0x81
     field public static final int KEYCODE_MEDIA_FAST_FORWARD = 90; // 0x5a
@@ -26537,6 +26630,9 @@
     method public void buildDrawingCache(boolean);
     method public void buildLayer();
     method public boolean callOnClick();
+    method public boolean canResolveLayoutDirection();
+    method public boolean canResolveTextAlignment();
+    method public boolean canResolveTextDirection();
     method public boolean canScrollHorizontally(int);
     method public boolean canScrollVertically(int);
     method public void cancelLongPress();
@@ -26711,7 +26807,6 @@
     method public float getY();
     method public boolean hasFocus();
     method public boolean hasFocusable();
-    method public boolean hasLayout();
     method public boolean hasOnClickListeners();
     method public boolean hasOverlappingRendering();
     method public boolean hasTransientState();
@@ -26741,6 +26836,8 @@
     method public boolean isInEditMode();
     method public boolean isInLayout();
     method public boolean isInTouchMode();
+    method public boolean isLaidOut();
+    method public boolean isLayoutDirectionResolved();
     method public boolean isLayoutRequested();
     method public boolean isLongClickable();
     method public boolean isOpaque();
@@ -26754,6 +26851,8 @@
     method public boolean isSelected();
     method public boolean isShown();
     method public boolean isSoundEffectsEnabled();
+    method public boolean isTextAlignmentResolved();
+    method public boolean isTextDirectionResolved();
     method public boolean isVerticalFadingEdgeEnabled();
     method public boolean isVerticalScrollBarEnabled();
     method public void jumpDrawablesToCurrentState();
@@ -27015,6 +27114,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;
@@ -27257,7 +27357,9 @@
     method public void bringChildToFront(android.view.View);
     method protected boolean canAnimate();
     method protected boolean checkLayoutParams(android.view.ViewGroup.LayoutParams);
+    method public void childAccessibilityStateChanged(android.view.View);
     method public void childDrawableStateChanged(android.view.View);
+    method public void childHasTransientStateChanged(android.view.View, boolean);
     method protected void cleanupLayoutState(android.view.View);
     method public void clearChildFocus(android.view.View);
     method public void clearDisappearingChildren();
@@ -27418,17 +27520,28 @@
 
   public abstract interface ViewParent {
     method public abstract void bringChildToFront(android.view.View);
+    method public abstract boolean canResolveLayoutDirection();
+    method public abstract boolean canResolveTextAlignment();
+    method public abstract boolean canResolveTextDirection();
+    method public abstract void childAccessibilityStateChanged(android.view.View);
     method public abstract void childDrawableStateChanged(android.view.View);
+    method public abstract void childHasTransientStateChanged(android.view.View, boolean);
     method public abstract void clearChildFocus(android.view.View);
     method public abstract void createContextMenu(android.view.ContextMenu);
     method public abstract android.view.View focusSearch(android.view.View, int);
     method public abstract void focusableViewAvailable(android.view.View);
     method public abstract boolean getChildVisibleRect(android.view.View, android.graphics.Rect, android.graphics.Point);
+    method public abstract int getLayoutDirection();
     method public abstract android.view.ViewParent getParent();
     method public abstract android.view.ViewParent getParentForAccessibility();
+    method public abstract int getTextAlignment();
+    method public abstract int getTextDirection();
     method public abstract void invalidateChild(android.view.View, android.graphics.Rect);
     method public abstract android.view.ViewParent invalidateChildInParent(int[], android.graphics.Rect);
+    method public abstract boolean isLayoutDirectionResolved();
     method public abstract boolean isLayoutRequested();
+    method public abstract boolean isTextAlignmentResolved();
+    method public abstract boolean isTextDirectionResolved();
     method public abstract void recomputeViewAttributes(android.view.View);
     method public abstract void requestChildFocus(android.view.View, android.view.View);
     method public abstract boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
@@ -27807,6 +27920,7 @@
     field public static final int TYPE_KEYGUARD_DIALOG = 2009; // 0x7d9
     field public static final int TYPE_PHONE = 2002; // 0x7d2
     field public static final int TYPE_PRIORITY_PHONE = 2007; // 0x7d7
+    field public static final int TYPE_PRIVATE_PRESENTATION = 2030; // 0x7ee
     field public static final int TYPE_SEARCH_BAR = 2001; // 0x7d1
     field public static final int TYPE_STATUS_BAR = 2000; // 0x7d0
     field public static final int TYPE_STATUS_BAR_PANEL = 2014; // 0x7de
@@ -28827,6 +28941,7 @@
     method public void setFadeBehavior(int);
     method public void setResizeBehavior(int);
     field public static final int FADE_BEHAVIOR_CROSSFADE = 0; // 0x0
+    field public static final int FADE_BEHAVIOR_OUT_IN = 2; // 0x2
     field public static final int FADE_BEHAVIOR_REVEAL = 1; // 0x1
     field public static final int RESIZE_BEHAVIOR_NONE = 0; // 0x0
     field public static final int RESIZE_BEHAVIOR_SCALE = 1; // 0x1
@@ -28874,6 +28989,11 @@
   public class TextChange extends android.view.transition.Transition {
     ctor public TextChange();
     method protected void captureValues(android.view.transition.TransitionValues, boolean);
+    method public void setChangeBehavior(int);
+    field public static final int CHANGE_BEHAVIOR_IN = 2; // 0x2
+    field public static final int CHANGE_BEHAVIOR_KEEP = 0; // 0x0
+    field public static final int CHANGE_BEHAVIOR_OUT = 1; // 0x1
+    field public static final int CHANGE_BEHAVIOR_OUT_IN = 3; // 0x3
   }
 
   public abstract class Transition implements java.lang.Cloneable {
@@ -49945,6 +50065,7 @@
     ctor public JSONArray(java.util.Collection);
     ctor public JSONArray(org.json.JSONTokener) throws org.json.JSONException;
     ctor public JSONArray(java.lang.String) throws org.json.JSONException;
+    ctor public JSONArray(java.lang.Object) throws org.json.JSONException;
     method public java.lang.Object get(int) throws org.json.JSONException;
     method public boolean getBoolean(int) throws org.json.JSONException;
     method public double getDouble(int) throws org.json.JSONException;
@@ -49979,6 +50100,7 @@
     method public org.json.JSONArray put(int, int) throws org.json.JSONException;
     method public org.json.JSONArray put(int, long) throws org.json.JSONException;
     method public org.json.JSONArray put(int, java.lang.Object) throws org.json.JSONException;
+    method public java.lang.Object remove(int);
     method public org.json.JSONObject toJSONObject(org.json.JSONArray) throws org.json.JSONException;
     method public java.lang.String toString(int) throws org.json.JSONException;
   }
@@ -50031,6 +50153,7 @@
     method public java.lang.Object remove(java.lang.String);
     method public org.json.JSONArray toJSONArray(org.json.JSONArray) throws org.json.JSONException;
     method public java.lang.String toString(int) throws org.json.JSONException;
+    method public static java.lang.Object wrap(java.lang.Object);
     field public static final java.lang.Object NULL;
   }
 
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 87ea4fd..d945216 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -84,6 +84,7 @@
                 "               [--R COUNT] [-S] [--opengl-trace]\n" +
                 "               [--user <USER_ID> | current] <INTENT>\n" +
                 "       am startservice [--user <USER_ID> | current] <INTENT>\n" +
+                "       am stopservice [--user <USER_ID> | current] <INTENT>\n" +
                 "       am force-stop [--user <USER_ID> | all | current] <PACKAGE>\n" +
                 "       am kill [--user <USER_ID> | all | current] <PACKAGE>\n" +
                 "       am kill-all\n" +
@@ -125,6 +126,10 @@
                 "    --user <USER_ID> | current: Specify which user to run as; if not\n" +
                 "        specified then run as the current user.\n" +
                 "\n" +
+                "am stopservice: stop a Service.  Options are:\n" +
+                "    --user <USER_ID> | current: Specify which user to run as; if not\n" +
+                "        specified then run as the current user.\n" +
+                "\n" +
                 "am force-stop: force stop everything associated with <PACKAGE>.\n" +
                 "    --user <USER_ID> | all | current: Specify user to force stop;\n" +
                 "        all users if not specified.\n" +
@@ -259,6 +264,8 @@
             runStart();
         } else if (op.equals("startservice")) {
             runStartService();
+        } else if (op.equals("stopservice")) {
+            runStopService();
         } else if (op.equals("force-stop")) {
             runForceStop();
         } else if (op.equals("kill")) {
@@ -574,6 +581,23 @@
         }
     }
 
+    private void runStopService() throws Exception {
+        Intent intent = makeIntent(UserHandle.USER_CURRENT);
+        if (mUserId == UserHandle.USER_ALL) {
+            System.err.println("Error: Can't stop activity with user 'all'");
+            return;
+        }
+        System.out.println("Stopping service: " + intent);
+        int result = mAm.stopService(null, intent, intent.getType(), mUserId);
+        if (result == 0) {
+            System.err.println("Service not stopped: was not running.");
+        } else if (result == 1) {
+            System.err.println("Service stopped");
+        } else if (result == -1) {
+            System.err.println("Error stopping service");
+        }
+    }
+
     private void runStart() throws Exception {
         Intent intent = makeIntent(UserHandle.USER_CURRENT);
 
diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java
index bb26443..473b60c 100644
--- a/cmds/input/src/com/android/commands/input/Input.java
+++ b/cmds/input/src/com/android/commands/input/Input.java
@@ -24,6 +24,9 @@
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 
+import java.util.HashMap;
+import java.util.Map;
+
 /**
  * Command that sends key events to the device, either by their keycode, or by
  * desired character output.
@@ -31,6 +34,21 @@
 
 public class Input {
     private static final String TAG = "Input";
+    private static final String INVALID_ARGUMENTS = "Error: Invalid arguments for command: ";
+
+    private static final Map<String, Integer> SOURCES = new HashMap<String, Integer>() {{
+        put("keyboard", InputDevice.SOURCE_KEYBOARD);
+        put("dpad", InputDevice.SOURCE_DPAD);
+        put("gamepad", InputDevice.SOURCE_GAMEPAD);
+        put("touchscreen", InputDevice.SOURCE_TOUCHSCREEN);
+        put("mouse", InputDevice.SOURCE_MOUSE);
+        put("stylus", InputDevice.SOURCE_STYLUS);
+        put("trackball", InputDevice.SOURCE_TRACKBALL);
+        put("touchpad", InputDevice.SOURCE_TOUCHPAD);
+        put("touchnavigation", InputDevice.SOURCE_TOUCH_NAVIGATION);
+        put("joystick", InputDevice.SOURCE_JOYSTICK);
+    }};
+
 
     /**
      * Command-line entry point.
@@ -47,86 +65,71 @@
             return;
         }
 
-        String command = args[0];
+        int index = 0;
+        String command = args[index];
+        int inputSource = InputDevice.SOURCE_UNKNOWN;
+        if (SOURCES.containsKey(command)) {
+            inputSource = SOURCES.get(command);
+            index++;
+            command = args[index];
+        }
+        final int length = args.length - index;
 
         try {
             if (command.equals("text")) {
-                if (args.length == 2) {
-                    sendText(args[1]);
+                if (length == 2) {
+                    inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
+                    sendText(inputSource, args[index+1]);
                     return;
                 }
             } else if (command.equals("keyevent")) {
-                if (args.length >= 2) {
-                    final boolean longpress = "--longpress".equals(args[1]);
-                    final int start = longpress ? 2 : 1;
-                    if (args.length > start) {
-                        for (int i = start; i < args.length; i++) {
+                if (length >= 2) {
+                    final boolean longpress = "--longpress".equals(args[index + 1]);
+                    final int start = longpress ? index + 2 : index + 1;
+                    inputSource = getSource(inputSource, InputDevice.SOURCE_KEYBOARD);
+                    if (length > start) {
+                        for (int i = start; i < length; i++) {
                             int keyCode = KeyEvent.keyCodeFromString(args[i]);
                             if (keyCode == KeyEvent.KEYCODE_UNKNOWN) {
                                 keyCode = KeyEvent.keyCodeFromString("KEYCODE_" + args[i]);
                             }
-                            sendKeyEvent(keyCode, longpress);
+                            sendKeyEvent(inputSource, keyCode, longpress);
                         }
                         return;
                     }
                 }
             } else if (command.equals("tap")) {
-                if (args.length == 3) {
-                    sendTap(InputDevice.SOURCE_TOUCHSCREEN, Float.parseFloat(args[1]), Float.parseFloat(args[2]));
+                if (length == 3) {
+                    inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+                    sendTap(inputSource, Float.parseFloat(args[index+1]),
+                            Float.parseFloat(args[index+2]));
                     return;
                 }
             } else if (command.equals("swipe")) {
-                if (args.length == 5) {
-                    sendSwipe(InputDevice.SOURCE_TOUCHSCREEN, Float.parseFloat(args[1]), Float.parseFloat(args[2]),
-                            Float.parseFloat(args[3]), Float.parseFloat(args[4]), -1);
+                int duration = -1;
+                inputSource = getSource(inputSource, InputDevice.SOURCE_TOUCHSCREEN);
+                switch (length) {
+                    case 6:
+                        duration = Integer.parseInt(args[index+5]);
+                    case 5:
+                        sendSwipe(inputSource,
+                                Float.parseFloat(args[index+1]), Float.parseFloat(args[index+2]),
+                                Float.parseFloat(args[index+3]), Float.parseFloat(args[index+4]),
+                                duration);
+                        return;
+                }
+            } else if (command.equals("press")) {
+                inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+                if (length == 1) {
+                    sendTap(inputSource, 0.0f, 0.0f);
                     return;
                 }
-            } else if (command.equals("touchscreen") || command.equals("touchpad")
-                    || command.equals("touchnavigation")) {
-                // determine input source
-                int inputSource = InputDevice.SOURCE_TOUCHSCREEN;
-                if (command.equals("touchpad")) {
-                    inputSource = InputDevice.SOURCE_TOUCHPAD;
-                } else if (command.equals("touchnavigation")) {
-                    inputSource = InputDevice.SOURCE_TOUCH_NAVIGATION;
-                }
-                // determine subcommand
-                if (args.length > 1) {
-                    String subcommand = args[1];
-                    if (subcommand.equals("tap")) {
-                        if (args.length == 4) {
-                            sendTap(inputSource, Float.parseFloat(args[2]),
-                                    Float.parseFloat(args[3]));
-                            return;
-                        }
-                    } else if (subcommand.equals("swipe")) {
-                        if (args.length == 6) {
-                            sendSwipe(inputSource, Float.parseFloat(args[2]),
-                                    Float.parseFloat(args[3]), Float.parseFloat(args[4]),
-                                    Float.parseFloat(args[5]), -1);
-                            return;
-                        } else if (args.length == 7) {
-                            sendSwipe(inputSource, Float.parseFloat(args[2]),
-                                    Float.parseFloat(args[3]), Float.parseFloat(args[4]),
-                                    Float.parseFloat(args[5]), Integer.parseInt(args[6]));
-                            return;
-                        }
-                    }
-                }
-            } else if (command.equals("trackball")) {
-                // determine subcommand
-                if (args.length > 1) {
-                    String subcommand = args[1];
-                    if (subcommand.equals("press")) {
-                        sendTap(InputDevice.SOURCE_TRACKBALL, 0.0f, 0.0f);
-                        return;
-                    } else if (subcommand.equals("roll")) {
-                        if (args.length == 4) {
-                            sendMove(InputDevice.SOURCE_TRACKBALL, Float.parseFloat(args[2]),
-                                    Float.parseFloat(args[3]));
-                            return;
-                        }
-                    }
+            } else if (command.equals("roll")) {
+                inputSource = getSource(inputSource, InputDevice.SOURCE_TRACKBALL);
+                if (length == 3) {
+                    sendMove(inputSource, Float.parseFloat(args[index+1]),
+                            Float.parseFloat(args[index+2]));
+                    return;
                 }
             } else {
                 System.err.println("Error: Unknown command: " + command);
@@ -135,7 +138,7 @@
             }
         } catch (NumberFormatException ex) {
         }
-        System.err.println("Error: Invalid arguments for command: " + command);
+        System.err.println(INVALID_ARGUMENTS + command);
         showUsage();
     }
 
@@ -145,7 +148,7 @@
      *
      * @param text is a string of characters you want to input to the device.
      */
-    private void sendText(String text) {
+    private void sendText(int source, String text) {
 
         StringBuffer buff = new StringBuffer(text);
 
@@ -157,7 +160,7 @@
                     buff.setCharAt(i, ' ');
                     buff.deleteCharAt(--i);
                 }
-            } 
+            }
             if (buff.charAt(i) == '%') {
                 escapeFlag = true;
             }
@@ -168,21 +171,25 @@
         KeyCharacterMap kcm = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
         KeyEvent[] events = kcm.getEvents(chars);
         for(int i = 0; i < events.length; i++) {
-            injectKeyEvent(events[i]);
+            KeyEvent e = events[i];
+            if (source != e.getSource()) {
+                e.setSource(source);
+            }
+            injectKeyEvent(e);
         }
     }
 
-    private void sendKeyEvent(int keyCode, boolean longpress) {
+    private void sendKeyEvent(int inputSource, int keyCode, boolean longpress) {
         long now = SystemClock.uptimeMillis();
         injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 0, 0,
-                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
+                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
         if (longpress) {
             injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_DOWN, keyCode, 1, 0,
                     KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_LONG_PRESS,
-                    InputDevice.SOURCE_KEYBOARD));
+                    inputSource));
         }
         injectKeyEvent(new KeyEvent(now, now, KeyEvent.ACTION_UP, keyCode, 0, 0,
-                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
+                KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, inputSource));
     }
 
     private void sendTap(int inputSource, float x, float y) {
@@ -206,7 +213,7 @@
                     lerp(y1, y2, alpha), 1.0f);
             now = SystemClock.uptimeMillis();
         }
-        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x1, y1, 0.0f);
+        injectMotionEvent(inputSource, MotionEvent.ACTION_UP, now, x2, y2, 0.0f);
     }
 
     /**
@@ -257,14 +264,26 @@
         return (b - a) * alpha + a;
     }
 
+    private static final int getSource(int inputSource, int defaultSource) {
+        return inputSource == -1 ? defaultSource : inputSource;
+    }
+
     private void showUsage() {
-        System.err.println("usage: input ...");
-        System.err.println("       input text <string>");
-        System.err.println("       input keyevent [--longpress] <key code number or name> ...");
-        System.err.println("       input [touchscreen|touchpad|touchnavigation] tap <x> <y>");
-        System.err.println("       input [touchscreen|touchpad|touchnavigation] swipe "
-                + "<x1> <y1> <x2> <y2> [duration(ms)]");
-        System.err.println("       input trackball press");
-        System.err.println("       input trackball roll <dx> <dy>");
+        System.err.println("Usage: input [<source>] <command> [<arg>...]");
+        System.err.println();
+        System.err.println("The sources are: ");
+        for (String src : SOURCES.keySet()) {
+            System.err.println("      " + src);
+        }
+        System.err.println();
+        System.err.println("The commands and default sources are:");
+        System.err.println("      text <string> (Default: touchscreen)");
+        System.err.println("      keyevent [--longpress] <key code number or name> ..."
+                + " (Default: keyboard)");
+        System.err.println("      tap <x> <y> (Default: touchscreen)");
+        System.err.println("      swipe <x1> <y1> <x2> <y2> [duration(ms)]"
+                + " (Default: touchscreen)");
+        System.err.println("      press (Default: trackball)");
+        System.err.println("      roll <dx> <dy> (Default: trackball)");
     }
 }
diff --git a/cmds/system_server/Android.mk b/cmds/system_server/Android.mk
deleted file mode 100644
index 3083e31..0000000
--- a/cmds/system_server/Android.mk
+++ /dev/null
@@ -1,20 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	system_main.cpp
-
-LOCAL_SHARED_LIBRARIES := \
-	libutils \
-	libbinder \
-	libsystem_server \
-	liblog
-
-LOCAL_C_INCLUDES := \
-	$(JNI_H_INCLUDE)
-
-LOCAL_MODULE:= system_server
-
-include $(BUILD_EXECUTABLE)
-
-include $(LOCAL_PATH)/library/Android.mk
diff --git a/cmds/system_server/MODULE_LICENSE_APACHE2 b/cmds/system_server/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/cmds/system_server/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/cmds/system_server/NOTICE b/cmds/system_server/NOTICE
deleted file mode 100644
index c5b1efa..0000000
--- a/cmds/system_server/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2005-2008, 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.
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/cmds/system_server/library/Android.mk b/cmds/system_server/library/Android.mk
deleted file mode 100644
index d78474e..0000000
--- a/cmds/system_server/library/Android.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	system_init.cpp
-
-base = $(LOCAL_PATH)/../../..
-native = $(LOCAL_PATH)/../../../../native
-
-LOCAL_C_INCLUDES := \
-	$(native)/services/sensorservice \
-	$(native)/services/surfaceflinger \
-	$(JNI_H_INCLUDE)
-
-LOCAL_SHARED_LIBRARIES := \
-	libandroid_runtime \
-	libsensorservice \
-	libsurfaceflinger \
-	libinput \
-	libutils \
-	libbinder \
-	libcutils \
-	liblog
-
-LOCAL_MODULE:= libsystem_server
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/cmds/system_server/library/system_init.cpp b/cmds/system_server/library/system_init.cpp
deleted file mode 100644
index 699996a..0000000
--- a/cmds/system_server/library/system_init.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * System server main initialization.
- *
- * The system server is responsible for becoming the Binder
- * context manager, supplying the root ServiceManager object
- * through which other services can be found.
- */
-
-#define LOG_TAG "sysproc"
-
-#include <binder/IPCThreadState.h>
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <binder/TextOutput.h>
-#include <utils/Log.h>
-
-#include <SurfaceFlinger.h>
-#include <SensorService.h>
-
-#include <android_runtime/AndroidRuntime.h>
-
-#include <signal.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/time.h>
-#include <cutils/properties.h>
-
-using namespace android;
-
-namespace android {
-/**
- * This class is used to kill this process when the runtime dies.
- */
-class GrimReaper : public IBinder::DeathRecipient {
-public:
-    GrimReaper() { }
-
-    virtual void binderDied(const wp<IBinder>& who)
-    {
-        ALOGI("Grim Reaper killing system_server...");
-        kill(getpid(), SIGKILL);
-    }
-};
-
-} // namespace android
-
-
-
-extern "C" status_t system_init()
-{
-    ALOGI("Entered system_init()");
-
-    sp<ProcessState> proc(ProcessState::self());
-
-    sp<IServiceManager> sm = defaultServiceManager();
-    ALOGI("ServiceManager: %p\n", sm.get());
-
-    sp<GrimReaper> grim = new GrimReaper();
-    sm->asBinder()->linkToDeath(grim, grim.get(), 0);
-
-    char propBuf[PROPERTY_VALUE_MAX];
-    property_get("system_init.startsensorservice", propBuf, "1");
-    if (strcmp(propBuf, "1") == 0) {
-        // Start the sensor service
-        SensorService::instantiate();
-    }
-
-    // And now start the Android runtime.  We have to do this bit
-    // of nastiness because the Android runtime initialization requires
-    // some of the core system services to already be started.
-    // All other servers should just start the Android runtime at
-    // the beginning of their processes's main(), before calling
-    // the init function.
-    ALOGI("System server: starting Android runtime.\n");
-    AndroidRuntime* runtime = AndroidRuntime::getRuntime();
-
-    ALOGI("System server: starting Android services.\n");
-    JNIEnv* env = runtime->getJNIEnv();
-    if (env == NULL) {
-        return UNKNOWN_ERROR;
-    }
-    jclass clazz = env->FindClass("com/android/server/SystemServer");
-    if (clazz == NULL) {
-        return UNKNOWN_ERROR;
-    }
-    jmethodID methodId = env->GetStaticMethodID(clazz, "init2", "()V");
-    if (methodId == NULL) {
-        return UNKNOWN_ERROR;
-    }
-
-    ALOGI("System server: entering thread pool.");
-    ProcessState::self()->startThreadPool();
-
-    // This is the main thread of the system server, and will never exit.
-    env->CallStaticVoidMethod(clazz, methodId);
-
-    return NO_ERROR;
-}
diff --git a/cmds/system_server/system_main.cpp b/cmds/system_server/system_main.cpp
deleted file mode 100644
index ddff065..0000000
--- a/cmds/system_server/system_main.cpp
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Main entry of system server process.
- * 
- * Calls the standard system initialization function, and then
- * puts the main thread into the thread pool so it can handle
- * incoming transactions.
- * 
- */
-
-#define LOG_TAG "sysproc"
-
-#include <binder/IPCThreadState.h>
-#include <utils/Log.h>
-
-#include <private/android_filesystem_config.h>
-
-#include <sys/time.h>
-#include <sys/resource.h>
-
-#include <signal.h>
-#include <stdio.h>
-#include <unistd.h>
-
-using namespace android;
-
-extern "C" status_t system_init();
-
-bool finish_system_init()
-{
-    return true;
-}
-
-static void blockSignals()
-{
-    sigset_t mask;
-    int cc;
-    
-    sigemptyset(&mask);
-    sigaddset(&mask, SIGQUIT);
-    sigaddset(&mask, SIGUSR1);
-    cc = sigprocmask(SIG_BLOCK, &mask, NULL);
-    assert(cc == 0);
-}
-
-int main(int argc, const char* const argv[])
-{
-    ALOGI("System server is starting with pid=%d.\n", getpid());
-
-    blockSignals();
-    
-    // You can trust me, honestly!
-    ALOGW("*** Current priority: %d\n", getpriority(PRIO_PROCESS, 0));
-    setpriority(PRIO_PROCESS, 0, -1);
-
-    system_init();    
-}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index d1efd0d1..8f2f9ca 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.util.ArrayMap;
 import com.android.internal.app.ActionBarImpl;
 import com.android.internal.policy.PolicyManager;
 
@@ -700,7 +701,7 @@
         Object activity;
         HashMap<String, Object> children;
         ArrayList<Fragment> fragments;
-        HashMap<String, LoaderManagerImpl> loaders;
+        ArrayMap<String, LoaderManagerImpl> loaders;
     }
     /* package */ NonConfigurationInstances mLastNonConfigurationInstances;
     
@@ -725,7 +726,7 @@
         }
     };
     
-    HashMap<String, LoaderManagerImpl> mAllLoaderManagers;
+    ArrayMap<String, LoaderManagerImpl> mAllLoaderManagers;
     LoaderManagerImpl mLoaderManager;
     
     private static final class ManagedCursor {
@@ -745,6 +746,7 @@
     // protected by synchronized (this) 
     int mResultCode = RESULT_CANCELED;
     Intent mResultData = null;
+    private TranslucentConversionListener mTranslucentCallback;
 
     private boolean mTitleReady = false;
 
@@ -818,13 +820,13 @@
             return mLoaderManager;
         }
         mCheckedForLoaderManager = true;
-        mLoaderManager = getLoaderManager(null, mLoadersStarted, true);
+        mLoaderManager = getLoaderManager("(root)", mLoadersStarted, true);
         return mLoaderManager;
     }
     
     LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
         if (mAllLoaderManagers == null) {
-            mAllLoaderManagers = new HashMap<String, LoaderManagerImpl>();
+            mAllLoaderManagers = new ArrayMap<String, LoaderManagerImpl>();
         }
         LoaderManagerImpl lm = mAllLoaderManagers.get(who);
         if (lm == null) {
@@ -1035,7 +1037,7 @@
             if (mLoaderManager != null) {
                 mLoaderManager.doStart();
             } else if (!mCheckedForLoaderManager) {
-                mLoaderManager = getLoaderManager(null, mLoadersStarted, false);
+                mLoaderManager = getLoaderManager("(root)", mLoadersStarted, false);
             }
             mCheckedForLoaderManager = true;
         }
@@ -1382,6 +1384,7 @@
         if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
         if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
         getApplication().dispatchActivityStopped(this);
+        mTranslucentCallback = null;
         mCalled = true;
     }
 
@@ -1646,17 +1649,18 @@
         if (mAllLoaderManagers != null) {
             // prune out any loader managers that were already stopped and so
             // have nothing useful to retain.
-            LoaderManagerImpl loaders[] = new LoaderManagerImpl[mAllLoaderManagers.size()];
-            mAllLoaderManagers.values().toArray(loaders);
-            if (loaders != null) {
-                for (int i=0; i<loaders.length; i++) {
-                    LoaderManagerImpl lm = loaders[i];
-                    if (lm.mRetaining) {
-                        retainLoaders = true;
-                    } else {
-                        lm.doDestroy();
-                        mAllLoaderManagers.remove(lm.mWho);
-                    }
+            final int N = mAllLoaderManagers.size();
+            LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
+            for (int i=N-1; i>=0; i--) {
+                loaders[i] = mAllLoaderManagers.valueAt(i);
+            }
+            for (int i=0; i<N; i++) {
+                LoaderManagerImpl lm = loaders[i];
+                if (lm.mRetaining) {
+                    retainLoaders = true;
+                } else {
+                    lm.doDestroy();
+                    mAllLoaderManagers.remove(lm.mWho);
                 }
             }
         }
@@ -4886,23 +4890,62 @@
     /**
      * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} to a
      * fullscreen opaque Activity.
-     *
+     * <p>
      * Call this whenever the background of a translucent Activity has changed to become opaque.
-     * Doing so will allow the previously visible Activity behind this one to be stopped. Stopped
-     * apps consume no CPU cycles and are eligible for removal when reclaiming memory.
-     *
+     * Doing so will allow the {@link android.view.Surface} of the Activity behind to be released.
+     * <p>
      * This call has no effect on non-translucent activities or on activities with the
      * {@link android.R.attr#windowIsFloating} attribute.
+     *
+     * @see #convertToTranslucent(TranslucentConversionListener)
+     * @see TranslucentConversionListener
      */
-    public void convertToOpaque() {
+    public void convertFromTranslucent() {
         try {
-            ActivityManagerNative.getDefault().convertToOpaque(mToken);
+            mTranslucentCallback = null;
+            ActivityManagerNative.getDefault().convertFromTranslucent(mToken);
         } catch (RemoteException e) {
             // pass
         }
     }
 
     /**
+     * Convert a translucent themed Activity {@link android.R.attr#windowIsTranslucent} back from
+     * opaque to translucent following a call to {@link #convertFromTranslucent()}.
+     * <p>
+     * Calling this allows the Activity behind this one to be seen again. Once all such Activities
+     * have been redrawn {@link TranslucentConversionListener#onTranslucentConversionComplete} will
+     * be called indicating that it is safe to make this activity translucent again. Until
+     * {@link TranslucentConversionListener#onTranslucentConversionComplete} is called the image
+     * behind the frontmost Activity will be indeterminate.
+     * <p>
+     * This call has no effect on non-translucent activities or on activities with the
+     * {@link android.R.attr#windowIsFloating} attribute.
+     *
+     * @param callback the method to call when all visible Activities behind this one have been
+     * drawn and it is safe to make this Activity translucent again.
+     *
+     * @see #convertFromTranslucent()
+     * @see TranslucentConversionListener
+     */
+    public void convertToTranslucent(TranslucentConversionListener callback) {
+        try {
+            mTranslucentCallback = callback;
+            ActivityManagerNative.getDefault().convertToTranslucent(mToken);
+        } catch (RemoteException e) {
+            // pass
+        }
+    }
+
+    /** @hide */
+    void onTranslucentConversionComplete(boolean drawComplete) {
+        if (mTranslucentCallback != null) {
+            mTranslucentCallback.onTranslucentConversionComplete(drawComplete);
+            mTranslucentCallback = null;
+        }
+    }
+
+    /**
      * Adjust the current immersive mode setting.
      *
      * Note that changing this value will have no effect on the activity's
@@ -4947,6 +4990,7 @@
      * @return The new action mode, or <code>null</code> if the activity does not want to
      *         provide special handling for this action mode. (It will be handled by the system.)
      */
+    @Override
     public ActionMode onWindowStartingActionMode(ActionMode.Callback callback) {
         initActionBar();
         if (mActionBar != null) {
@@ -4961,6 +5005,7 @@
      *
      * @param mode The new action mode.
      */
+    @Override
     public void onActionModeStarted(ActionMode mode) {
     }
 
@@ -4970,6 +5015,7 @@
      *
      * @param mode The action mode that just finished.
      */
+    @Override
     public void onActionModeFinished(ActionMode mode) {
     }
 
@@ -5192,14 +5238,15 @@
         }
         mFragments.dispatchStart();
         if (mAllLoaderManagers != null) {
-            LoaderManagerImpl loaders[] = new LoaderManagerImpl[mAllLoaderManagers.size()];
-            mAllLoaderManagers.values().toArray(loaders);
-            if (loaders != null) {
-                for (int i=0; i<loaders.length; i++) {
-                    LoaderManagerImpl lm = loaders[i];
-                    lm.finishRetain();
-                    lm.doReportStart();
-                }
+            final int N = mAllLoaderManagers.size();
+            LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
+            for (int i=N-1; i>=0; i--) {
+                loaders[i] = mAllLoaderManagers.valueAt(i);
+            }
+            for (int i=0; i<N; i++) {
+                LoaderManagerImpl lm = loaders[i];
+                lm.finishRetain();
+                lm.doReportStart();
             }
         }
     }
@@ -5373,4 +5420,26 @@
             }
         }
     }
+
+    /**
+     * Interface for informing a translucent {@link Activity} once all visible activities below it
+     * have completed drawing. This is necessary only after an {@link Activity} has been made
+     * opaque using {@link Activity#convertFromTranslucent()} and before it has been drawn
+     * translucent again following a call to {@link
+     * Activity#convertToTranslucent(TranslucentConversionListener)}.
+     */
+    public interface TranslucentConversionListener {
+        /**
+         * Callback made following {@link Activity#convertToTranslucent} once all visible Activities
+         * below the top one have been redrawn. Following this callback it is safe to make the top
+         * Activity translucent because the underlying Activity has been drawn.
+         *
+         * @param drawComplete True if the background Activity has drawn itself. False if a timeout
+         * occurred waiting for the Activity to complete drawing.
+         *
+         * @see Activity#convertFromTranslucent()
+         * @see Activity#convertToTranslucent(TranslucentConversionListener)
+         */
+        public void onTranslucentConversionComplete(boolean drawComplete);
+    }
 }
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 5fa874b..4e6c3dc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -228,6 +228,56 @@
     /** @hide User operation call: given user id is the current user, can't be stopped. */
     public static final int USER_OP_IS_CURRENT = -2;
 
+    /** @hide Process is a persistent system process. */
+    public static final int PROCESS_STATE_PERSISTENT = 0;
+
+    /** @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 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_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 backup/restore operation. */
+    public static final int PROCESS_STATE_BACKUP = 5;
+
+    /** @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 = 9;
+
+    /** @hide Process is in the background but hosts the last shown activity. */
+    public static final int PROCESS_STATE_LAST_ACTIVITY = 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;
         mHandler = handler;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index a23611ec..acfcb40 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1499,10 +1499,18 @@
             return true;
         }
 
-        case CONVERT_TO_OPAQUE_TRANSACTION: {
+        case CONVERT_FROM_TRANSLUCENT_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             IBinder token = data.readStrongBinder();
-            convertToOpaque(token);
+            convertFromTranslucent(token);
+            reply.writeNoException();
+            return true;
+        }
+
+        case CONVERT_TO_TRANSLUCENT_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            convertToTranslucent(token);
             reply.writeNoException();
             return true;
         }
@@ -1957,6 +1965,13 @@
             return true;
         }
 
+        case NOTIFY_ACTIVITY_DRAWN_TRANSACTION: {
+            data.enforceInterface(IActivityManager.descriptor);
+            IBinder token = data.readStrongBinder();
+            notifyActivityDrawn(token);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -3840,13 +3855,25 @@
         reply.recycle();
     }
 
-    public void convertToOpaque(IBinder token)
+    public void convertFromTranslucent(IBinder token)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
         data.writeInterfaceToken(IActivityManager.descriptor);
         data.writeStrongBinder(token);
-        mRemote.transact(CONVERT_TO_OPAQUE_TRANSACTION, data, reply, 0);
+        mRemote.transact(CONVERT_FROM_TRANSLUCENT_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    public void convertToTranslucent(IBinder token)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(CONVERT_TO_TRANSLUCENT_TRANSACTION, data, reply, 0);
         reply.readException();
         data.recycle();
         reply.recycle();
@@ -4482,5 +4509,16 @@
         reply.recycle();
     }
 
+    public void notifyActivityDrawn(IBinder token) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        data.writeStrongBinder(token);
+        mRemote.transact(NOTIFY_ACTIVITY_DRAWN_TRANSACTION, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
     private IBinder mRemote;
 }
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 4568525..d9f9d61 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -16,8 +16,6 @@
 
 package android.app;
 
-import static android.view.DisplayAdjustments.DEVELOPMENT_RESOURCES_DEPEND_ON_ACTIVITY_TOKEN;
-
 import android.app.backup.BackupAgent;
 import android.content.BroadcastReceiver;
 import android.content.ComponentCallbacks2;
@@ -71,13 +69,13 @@
 import android.os.Trace;
 import android.os.UserHandle;
 import android.util.AndroidRuntimeException;
+import android.util.ArrayMap;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
 import android.util.LogPrinter;
 import android.util.PrintWriterPrinter;
 import android.util.Slog;
-import android.view.DisplayAdjustments;
 import android.view.Display;
 import android.view.HardwareRenderer;
 import android.view.View;
@@ -106,8 +104,6 @@
 import java.net.InetAddress;
 import java.security.Security;
 import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
@@ -150,7 +146,7 @@
     public static final boolean DEBUG_BROADCAST = false;
     private static final boolean DEBUG_RESULTS = false;
     private static final boolean DEBUG_BACKUP = false;
-    private static final boolean DEBUG_CONFIGURATION = false;
+    public static final boolean DEBUG_CONFIGURATION = false;
     private static final boolean DEBUG_SERVICE = false;
     private static final boolean DEBUG_MEMORY_TRIM = false;
     private static final boolean DEBUG_PROVIDER = false;
@@ -167,28 +163,26 @@
     final ApplicationThread mAppThread = new ApplicationThread();
     final Looper mLooper = Looper.myLooper();
     final H mH = new H();
-    final HashMap<IBinder, ActivityClientRecord> mActivities
-            = new HashMap<IBinder, ActivityClientRecord>();
+    final ArrayMap<IBinder, ActivityClientRecord> mActivities
+            = new ArrayMap<IBinder, ActivityClientRecord>();
     // List of new activities (via ActivityRecord.nextIdle) that should
     // be reported when next we idle.
     ActivityClientRecord mNewActivities = null;
     // Number of activities that are currently visible on-screen.
     int mNumVisibleActivities = 0;
-    final HashMap<IBinder, Service> mServices
-            = new HashMap<IBinder, Service>();
+    final ArrayMap<IBinder, Service> mServices
+            = new ArrayMap<IBinder, Service>();
     AppBindData mBoundApplication;
     Profiler mProfiler;
     int mCurDefaultDisplayDpi;
     boolean mDensityCompatMode;
     Configuration mConfiguration;
     Configuration mCompatConfiguration;
-    Configuration mResConfiguration;
-    CompatibilityInfo mResCompatibilityInfo;
     Application mInitialApplication;
     final ArrayList<Application> mAllApplications
             = new ArrayList<Application>();
     // set of instantiated backup agents, keyed by package name
-    final HashMap<String, BackupAgent> mBackupAgents = new HashMap<String, BackupAgent>();
+    final ArrayMap<String, BackupAgent> mBackupAgents = new ArrayMap<String, BackupAgent>();
     /** Reference to singleton {@link ActivityThread} */
     private static ActivityThread sCurrentActivityThread;
     Instrumentation mInstrumentation;
@@ -208,18 +202,16 @@
     // which means this lock gets held while the activity and window managers
     // holds their own lock.  Thus you MUST NEVER call back into the activity manager
     // or window manager or anything that depends on them while holding this lock.
-    final HashMap<String, WeakReference<LoadedApk>> mPackages
-            = new HashMap<String, WeakReference<LoadedApk>>();
-    final HashMap<String, WeakReference<LoadedApk>> mResourcePackages
-            = new HashMap<String, WeakReference<LoadedApk>>();
-    final HashMap<DisplayAdjustments, DisplayMetrics> mDefaultDisplayMetrics
-            = new HashMap<DisplayAdjustments, DisplayMetrics>();
-    final HashMap<ResourcesKey, WeakReference<Resources> > mActiveResources
-            = new HashMap<ResourcesKey, WeakReference<Resources> >();
+    final ArrayMap<String, WeakReference<LoadedApk>> mPackages
+            = new ArrayMap<String, WeakReference<LoadedApk>>();
+    final ArrayMap<String, WeakReference<LoadedApk>> mResourcePackages
+            = new ArrayMap<String, WeakReference<LoadedApk>>();
     final ArrayList<ActivityClientRecord> mRelaunchingActivities
             = new ArrayList<ActivityClientRecord>();
     Configuration mPendingConfiguration = null;
 
+    private final ResourcesManager mResourcesManager;
+
     private static final class ProviderKey {
         final String authority;
         final int userId;
@@ -245,17 +237,17 @@
     }
 
     // The lock of mProviderMap protects the following variables.
-    final HashMap<ProviderKey, ProviderClientRecord> mProviderMap
-        = new HashMap<ProviderKey, ProviderClientRecord>();
-    final HashMap<IBinder, ProviderRefCount> mProviderRefCountMap
-        = new HashMap<IBinder, ProviderRefCount>();
-    final HashMap<IBinder, ProviderClientRecord> mLocalProviders
-        = new HashMap<IBinder, ProviderClientRecord>();
-    final HashMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
-            = new HashMap<ComponentName, ProviderClientRecord>();
+    final ArrayMap<ProviderKey, ProviderClientRecord> mProviderMap
+        = new ArrayMap<ProviderKey, ProviderClientRecord>();
+    final ArrayMap<IBinder, ProviderRefCount> mProviderRefCountMap
+        = new ArrayMap<IBinder, ProviderRefCount>();
+    final ArrayMap<IBinder, ProviderClientRecord> mLocalProviders
+        = new ArrayMap<IBinder, ProviderClientRecord>();
+    final ArrayMap<ComponentName, ProviderClientRecord> mLocalProvidersByName
+            = new ArrayMap<ComponentName, ProviderClientRecord>();
 
-    final HashMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
-        = new HashMap<Activity, ArrayList<OnActivityPausedListener>>();
+    final ArrayMap<Activity, ArrayList<OnActivityPausedListener>> mOnPauseListeners
+        = new ArrayMap<Activity, ArrayList<OnActivityPausedListener>>();
 
     final GcIdler mGcIdler = new GcIdler();
     boolean mGcIdlerScheduled = false;
@@ -554,8 +546,10 @@
         // Formatting for checkin service - update version if row format changes
         private static final int ACTIVITY_THREAD_CHECKIN_VERSION = 3;
 
+        private int mLastProcessState = -1;
+
         private void updatePendingConfiguration(Configuration config) {
-            synchronized (mPackages) {
+            synchronized (mResourcesManager) {
                 if (mPendingConfiguration == null ||
                         mPendingConfiguration.isOtherSeqNewer(config)) {
                     mPendingConfiguration = config;
@@ -589,7 +583,9 @@
             queueOrSendMessage(H.SLEEPING, token, sleeping ? 1 : 0);
         }
 
-        public final void scheduleResumeActivity(IBinder token, boolean isForward) {
+        public final void scheduleResumeActivity(IBinder token, int processState,
+                boolean isForward) {
+            updateProcessState(processState, false);
             queueOrSendMessage(H.RESUME_ACTIVITY, token, isForward ? 1 : 0);
         }
 
@@ -604,9 +600,12 @@
         // activity itself back to the activity manager. (matters more with ipc)
         public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                 ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-                Bundle state, List<ResultInfo> pendingResults,
+                int procState, Bundle state, List<ResultInfo> pendingResults,
                 List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
                 String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
+
+            updateProcessState(procState, false);
+
             ActivityClientRecord r = new ActivityClientRecord();
 
             r.token = token;
@@ -654,7 +653,8 @@
 
         public final void scheduleReceiver(Intent intent, ActivityInfo info,
                 CompatibilityInfo compatInfo, int resultCode, String data, Bundle extras,
-                boolean sync, int sendingUser) {
+                boolean sync, int sendingUser, int processState) {
+            updateProcessState(processState, false);
             ReceiverData r = new ReceiverData(intent, resultCode, data, extras,
                     sync, false, mAppThread.asBinder(), sendingUser);
             r.info = info;
@@ -682,7 +682,8 @@
         }
 
         public final void scheduleCreateService(IBinder token,
-                ServiceInfo info, CompatibilityInfo compatInfo) {
+                ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
+            updateProcessState(processState, false);
             CreateServiceData s = new CreateServiceData();
             s.token = token;
             s.info = info;
@@ -692,7 +693,8 @@
         }
 
         public final void scheduleBindService(IBinder token, Intent intent,
-                boolean rebind) {
+                boolean rebind, int processState) {
+            updateProcessState(processState, false);
             BindServiceData s = new BindServiceData();
             s.token = token;
             s.intent = intent;
@@ -817,7 +819,8 @@
         // applies transaction ordering per object for such calls.
         public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
                 int resultCode, String dataStr, Bundle extras, boolean ordered,
-                boolean sticky, int sendingUser) throws RemoteException {
+                boolean sticky, int sendingUser, int processState) throws RemoteException {
+            updateProcessState(processState, false);
             receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                     sticky, sendingUser);
         }
@@ -1222,6 +1225,27 @@
             queueOrSendMessage(H.TRIM_MEMORY, null, level);
         }
 
+        public void scheduleTranslucentConversionComplete(IBinder token, boolean drawComplete) {
+            queueOrSendMessage(H.TRANSLUCENT_CONVERSION_COMPLETE, token, drawComplete ? 1 : 0);
+        }
+
+        public void setProcessState(int state) {
+            updateProcessState(state, true);
+        }
+
+        public void updateProcessState(int processState, boolean fromIpc) {
+            synchronized (this) {
+                if (mLastProcessState != processState) {
+                    mLastProcessState = processState;
+
+                    // Update Dalvik state here based on ActivityManager.PROCESS_STATE_* constants.
+                    if (false) {
+                        Slog.i(TAG, "******************* PROCESS STATE CHANGED TO: " + processState
+                                + (fromIpc ? " (from ipc": ""));
+                    }
+                }
+            }
+        }
     }
 
     private class H extends Handler {
@@ -1269,6 +1293,7 @@
         public static final int DUMP_PROVIDER           = 141;
         public static final int UNSTABLE_PROVIDER_DIED  = 142;
         public static final int REQUEST_ACTIVITY_EXTRAS = 143;
+        public static final int TRANSLUCENT_CONVERSION_COMPLETE = 144;
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
                 switch (code) {
@@ -1316,6 +1341,7 @@
                     case DUMP_PROVIDER: return "DUMP_PROVIDER";
                     case UNSTABLE_PROVIDER_DIED: return "UNSTABLE_PROVIDER_DIED";
                     case REQUEST_ACTIVITY_EXTRAS: return "REQUEST_ACTIVITY_EXTRAS";
+                    case TRANSLUCENT_CONVERSION_COMPLETE: return "TRANSLUCENT_CONVERSION_COMPLETE";
                 }
             }
             return Integer.toString(code);
@@ -1530,6 +1556,9 @@
                 case REQUEST_ACTIVITY_EXTRAS:
                     handleRequestActivityExtras((RequestActivityExtras)msg.obj);
                     break;
+                case TRANSLUCENT_CONVERSION_COMPLETE:
+                    handleTranslucentConversionComplete((IBinder)msg.obj, msg.arg1 == 1);
+                    break;
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
         }
@@ -1608,72 +1637,6 @@
         }
     }
 
-    private static class ResourcesKey {
-        final private String mResDir;
-        final private int mDisplayId;
-        final private Configuration mOverrideConfiguration;
-        final private float mScale;
-        final private int mHash;
-        final private IBinder mToken;
-
-        ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration,
-                float scale, IBinder token) {
-            mResDir = resDir;
-            mDisplayId = displayId;
-            if (overrideConfiguration != null) {
-                if (Configuration.EMPTY.equals(overrideConfiguration)) {
-                    overrideConfiguration = null;
-                }
-            }
-            mOverrideConfiguration = overrideConfiguration;
-            mScale = scale;
-            int hash = 17;
-            hash = 31 * hash + mResDir.hashCode();
-            hash = 31 * hash + mDisplayId;
-            hash = 31 * hash + (mOverrideConfiguration != null
-                    ? mOverrideConfiguration.hashCode() : 0);
-            hash = 31 * hash + Float.floatToIntBits(mScale);
-            if (DEVELOPMENT_RESOURCES_DEPEND_ON_ACTIVITY_TOKEN) {
-                mToken = token;
-                hash = 31 * hash + (mToken == null ? 0 : mToken.hashCode());
-            } else {
-                mToken = null;
-            }
-            mHash = hash;
-        }
-
-        @Override
-        public int hashCode() {
-            return mHash;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (!(obj instanceof ResourcesKey)) {
-                return false;
-            }
-            ResourcesKey peer = (ResourcesKey) obj;
-            if (!mResDir.equals(peer.mResDir)) {
-                return false;
-            }
-            if (mDisplayId != peer.mDisplayId) {
-                return false;
-            }
-            if (mOverrideConfiguration != peer.mOverrideConfiguration) {
-                if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) {
-                    return false;
-                }
-                if (!mOverrideConfiguration.equals(peer.mOverrideConfiguration)) {
-                    return false;
-                }
-            }
-            if (mScale != peer.mScale) {
-                return false;
-            }
-            return true;
-        }
-    }
-
     public static ActivityThread currentActivityThread() {
         return sCurrentActivityThread;
     }
@@ -1707,49 +1670,6 @@
         return sPackageManager;
     }
 
-    private void flushDisplayMetricsLocked() {
-        mDefaultDisplayMetrics.clear();
-    }
-
-    DisplayMetrics getDisplayMetricsLocked(int displayId) {
-        return getDisplayMetricsLocked(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
-    }
-
-    DisplayMetrics getDisplayMetricsLocked(int displayId, DisplayAdjustments daj) {
-        boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
-        DisplayMetrics dm = isDefaultDisplay ? mDefaultDisplayMetrics.get(daj) : null;
-        if (dm != null) {
-            return dm;
-        }
-        dm = new DisplayMetrics();
-
-        DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance();
-        if (displayManager == null) {
-            // may be null early in system startup
-            dm.setToDefaults();
-            return dm;
-        }
-
-        if (isDefaultDisplay) {
-            mDefaultDisplayMetrics.put(daj, dm);
-        }
-
-        Display d = displayManager.getCompatibleDisplay(displayId, daj);
-        if (d != null) {
-            d.getMetrics(dm);
-        } else {
-            // Display no longer exists
-            // FIXME: This would not be a problem if we kept the Display object around
-            // instead of using the raw display id everywhere.  The Display object caches
-            // its information even after the display has been removed.
-            dm.setToDefaults();
-        }
-        //Slog.i("foo", "New metrics: w=" + metrics.widthPixels + " h="
-        //        + metrics.heightPixels + " den=" + metrics.density
-        //        + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi);
-        return dm;
-    }
-
     private Configuration mMainThreadConfig = new Configuration();
     Configuration applyConfigCompatMainThread(int displayDensity, Configuration config,
             CompatibilityInfo compat) {
@@ -1765,90 +1685,12 @@
     }
 
     /**
-     * Creates the top level Resources for applications with the given compatibility info.
-     *
-     * @param resDir the resource directory.
-     * @param compatInfo the compability info. Must not be null.
-     * @param token the application token for determining stack bounds.
-     */
-    Resources getTopLevelResources(String resDir, int displayId,
-            Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
-        final float scale = compatInfo.applicationScale;
-        ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale,
-                token);
-        Resources r;
-        synchronized (mPackages) {
-            // Resources is app scale dependent.
-            if (false) {
-                Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
-            }
-            WeakReference<Resources> wr = mActiveResources.get(key);
-            r = wr != null ? wr.get() : null;
-            //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
-            if (r != null && r.getAssets().isUpToDate()) {
-                if (false) {
-                    Slog.w(TAG, "Returning cached resources " + r + " " + resDir
-                            + ": appScale=" + r.getCompatibilityInfo().applicationScale);
-                }
-                return r;
-            }
-        }
-
-        //if (r != null) {
-        //    Slog.w(TAG, "Throwing away out-of-date resources!!!! "
-        //            + r + " " + resDir);
-        //}
-
-        AssetManager assets = new AssetManager();
-        if (assets.addAssetPath(resDir) == 0) {
-            return null;
-        }
-
-        //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
-        DisplayMetrics dm = getDisplayMetricsLocked(displayId);
-        Configuration config;
-        boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
-        if (!isDefaultDisplay || key.mOverrideConfiguration != null) {
-            config = new Configuration(getConfiguration());
-            if (!isDefaultDisplay) {
-                applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
-            }
-            if (key.mOverrideConfiguration != null) {
-                config.updateFrom(key.mOverrideConfiguration);
-            }
-        } else {
-            config = getConfiguration();
-        }
-        r = new Resources(assets, dm, config, compatInfo, token);
-        if (false) {
-            Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
-                    + r.getConfiguration() + " appScale="
-                    + r.getCompatibilityInfo().applicationScale);
-        }
-
-        synchronized (mPackages) {
-            WeakReference<Resources> wr = mActiveResources.get(key);
-            Resources existing = wr != null ? wr.get() : null;
-            if (existing != null && existing.getAssets().isUpToDate()) {
-                // Someone else already created the resources while we were
-                // unlocked; go ahead and use theirs.
-                r.getAssets().close();
-                return existing;
-            }
-
-            // XXX need to remove entries when weak references go away
-            mActiveResources.put(key, new WeakReference<Resources>(r));
-            return r;
-        }
-    }
-
-    /**
      * Creates the top level resources for the given package.
      */
     Resources getTopLevelResources(String resDir,
             int displayId, Configuration overrideConfiguration,
             LoadedApk pkgInfo) {
-        return getTopLevelResources(resDir, displayId, overrideConfiguration,
+        return mResourcesManager.getTopLevelResources(resDir, displayId, overrideConfiguration,
                 pkgInfo.getCompatibilityInfo(), null);
     }
 
@@ -1863,7 +1705,7 @@
 
     public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
             int flags, int userId) {
-        synchronized (mPackages) {
+        synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
             if ((flags&Context.CONTEXT_INCLUDE_CODE) != 0) {
                 ref = mPackages.get(packageName);
@@ -1933,7 +1775,7 @@
     }
 
     public final LoadedApk peekPackageInfo(String packageName, boolean includeCode) {
-        synchronized (mPackages) {
+        synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
             if (includeCode) {
                 ref = mPackages.get(packageName);
@@ -1946,7 +1788,7 @@
 
     private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
             ClassLoader baseLoader, boolean securityViolation, boolean includeCode) {
-        synchronized (mPackages) {
+        synchronized (mResourcesManager) {
             WeakReference<LoadedApk> ref;
             if (includeCode) {
                 ref = mPackages.get(aInfo.packageName);
@@ -1978,6 +1820,7 @@
     }
 
     ActivityThread() {
+        mResourcesManager = ResourcesManager.getInstance();
     }
 
     public ApplicationThread getApplicationThread()
@@ -1990,10 +1833,6 @@
         return mInstrumentation;
     }
 
-    public Configuration getConfiguration() {
-        return mResConfiguration;
-    }
-
     public boolean isProfiling() {
         return mProfiler != null && mProfiler.profileFile != null
                 && mProfiler.profileFd == null;
@@ -2023,8 +1862,8 @@
                 LoadedApk info = new LoadedApk(this, "android", context, null,
                         CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO);
                 context.init(info, null, this);
-                context.getResources().updateConfiguration(getConfiguration(),
-                        getDisplayMetricsLocked(Display.DEFAULT_DISPLAY));
+                context.getResources().updateConfiguration(mResourcesManager.getConfiguration(),
+                        mResourcesManager.getDisplayMetricsLocked(Display.DEFAULT_DISPLAY));
                 mSystemContext = context;
                 //Slog.i(TAG, "Created system resources " + context.getResources()
                 //        + ": " + context.getResources().getConfiguration());
@@ -2452,7 +2291,14 @@
         } catch (RemoteException e) {
         }
     }
-    
+
+    public void handleTranslucentConversionComplete(IBinder token, boolean drawComplete) {
+        ActivityClientRecord r = mActivities.get(token);
+        if (r != null) {
+            r.activity.onTranslucentConversionComplete(drawComplete);
+        }
+    }
+
     private static final ThreadLocal<Intent> sCurrentBroadcastIntent = new ThreadLocal<Intent>();
 
     /**
@@ -3419,7 +3265,7 @@
     }
 
     private void handleSetCoreSettings(Bundle coreSettings) {
-        synchronized (mPackages) {
+        synchronized (mResourcesManager) {
             mCoreSettings = coreSettings;
         }
     }
@@ -3509,7 +3355,7 @@
     private ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
             int configChanges, boolean getNonConfigInstance) {
         ActivityClientRecord r = mActivities.get(token);
-        Class activityClass = null;
+        Class<? extends Activity> activityClass = null;
         if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
         if (r != null) {
             activityClass = r.activity.getClass();
@@ -3664,7 +3510,7 @@
             boolean fromServer) {
         ActivityClientRecord target = null;
 
-        synchronized (mPackages) {
+        synchronized (mResourcesManager) {
             for (int i=0; i<mRelaunchingActivities.size(); i++) {
                 ActivityClientRecord r = mRelaunchingActivities.get(i);
                 if (r.token == token) {
@@ -3725,7 +3571,7 @@
         // First: make sure we have the most recent configuration and most
         // recent version of the activity, or skip it if some previous call
         // had taken a more recent version.
-        synchronized (mPackages) {
+        synchronized (mResourcesManager) {
             int N = mRelaunchingActivities.size();
             IBinder token = tmp.token;
             tmp = null;
@@ -3854,48 +3700,46 @@
         ArrayList<ComponentCallbacks2> callbacks
                 = new ArrayList<ComponentCallbacks2>();
 
-        synchronized (mPackages) {
-            final int N = mAllApplications.size();
-            for (int i=0; i<N; i++) {
+        synchronized (mResourcesManager) {
+            final int NAPP = mAllApplications.size();
+            for (int i=0; i<NAPP; i++) {
                 callbacks.add(mAllApplications.get(i));
             }
-            if (mActivities.size() > 0) {
-                for (ActivityClientRecord ar : mActivities.values()) {
-                    Activity a = ar.activity;
-                    if (a != null) {
-                        Configuration thisConfig = applyConfigCompatMainThread(
-                                mCurDefaultDisplayDpi, newConfig,
-                                ar.packageInfo.getCompatibilityInfo());
-                        if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
-                            // If the activity is currently resumed, its configuration
-                            // needs to change right now.
-                            callbacks.add(a);
-                        } else if (thisConfig != null) {
-                            // Otherwise, we will tell it about the change
-                            // the next time it is resumed or shown.  Note that
-                            // the activity manager may, before then, decide the
-                            // activity needs to be destroyed to handle its new
-                            // configuration.
-                            if (DEBUG_CONFIGURATION) {
-                                Slog.v(TAG, "Setting activity "
-                                        + ar.activityInfo.name + " newConfig=" + thisConfig);
-                            }
-                            ar.newConfig = thisConfig;
+            final int NACT = mActivities.size();
+            for (int i=0; i<NACT; i++) {
+                ActivityClientRecord ar = mActivities.valueAt(i);
+                Activity a = ar.activity;
+                if (a != null) {
+                    Configuration thisConfig = applyConfigCompatMainThread(
+                            mCurDefaultDisplayDpi, newConfig,
+                            ar.packageInfo.getCompatibilityInfo());
+                    if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
+                        // If the activity is currently resumed, its configuration
+                        // needs to change right now.
+                        callbacks.add(a);
+                    } else if (thisConfig != null) {
+                        // Otherwise, we will tell it about the change
+                        // the next time it is resumed or shown.  Note that
+                        // the activity manager may, before then, decide the
+                        // activity needs to be destroyed to handle its new
+                        // configuration.
+                        if (DEBUG_CONFIGURATION) {
+                            Slog.v(TAG, "Setting activity "
+                                    + ar.activityInfo.name + " newConfig=" + thisConfig);
                         }
+                        ar.newConfig = thisConfig;
                     }
                 }
             }
-            if (mServices.size() > 0) {
-                for (Service service : mServices.values()) {
-                    callbacks.add(service);
-                }
+            final int NSVC = mServices.size();
+            for (int i=0; i<NSVC; i++) {
+                callbacks.add(mServices.valueAt(i));
             }
         }
         synchronized (mProviderMap) {
-            if (mLocalProviders.size() > 0) {
-                for (ProviderClientRecord providerClientRecord : mLocalProviders.values()) {
-                    callbacks.add(providerClientRecord.mLocalProvider);
-                }
+            final int NPRV = mLocalProviders.size();
+            for (int i=0; i<NPRV; i++) {
+                callbacks.add(mLocalProviders.valueAt(i).mLocalProvider);
             }
         }
 
@@ -3948,114 +3792,18 @@
     }
 
     public final void applyConfigurationToResources(Configuration config) {
-        synchronized (mPackages) {
-            applyConfigurationToResourcesLocked(config, null);
+        synchronized (mResourcesManager) {
+            mResourcesManager.applyConfigurationToResourcesLocked(config, null);
         }
     }
 
-    final boolean applyConfigurationToResourcesLocked(Configuration config,
-            CompatibilityInfo compat) {
-        if (mResConfiguration == null) {
-            mResConfiguration = new Configuration();
-        }
-        if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
-            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
-                    + mResConfiguration.seq + ", newSeq=" + config.seq);
-            return false;
-        }
-        int changes = mResConfiguration.updateFrom(config);
-        flushDisplayMetricsLocked();
-        DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked(Display.DEFAULT_DISPLAY);
-
-        if (compat != null && (mResCompatibilityInfo == null ||
-                !mResCompatibilityInfo.equals(compat))) {
-            mResCompatibilityInfo = compat;
-            changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
-                    | ActivityInfo.CONFIG_SCREEN_SIZE
-                    | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
-        }
-
-        // set it for java, this also affects newly created Resources
-        if (config.locale != null) {
-            Locale.setDefault(config.locale);
-        }
-
-        Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
-
-        ApplicationPackageManager.configurationChanged();
-        //Slog.i(TAG, "Configuration changed in " + currentPackageName());
-
-        Configuration tmpConfig = null;
-
-        Iterator<Map.Entry<ResourcesKey, WeakReference<Resources>>> it =
-                mActiveResources.entrySet().iterator();
-        while (it.hasNext()) {
-            Map.Entry<ResourcesKey, WeakReference<Resources>> entry = it.next();
-            Resources r = entry.getValue().get();
-            if (r != null) {
-                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
-                        + r + " config to: " + config);
-                int displayId = entry.getKey().mDisplayId;
-                boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
-                DisplayMetrics dm = defaultDisplayMetrics;
-                Configuration overrideConfig = entry.getKey().mOverrideConfiguration;
-                if (!isDefaultDisplay || overrideConfig != null) {
-                    if (tmpConfig == null) {
-                        tmpConfig = new Configuration();
-                    }
-                    tmpConfig.setTo(config);
-                    if (!isDefaultDisplay) {
-                        dm = getDisplayMetricsLocked(displayId);
-                        applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
-                    }
-                    if (overrideConfig != null) {
-                        tmpConfig.updateFrom(overrideConfig);
-                    }
-                    r.updateConfiguration(tmpConfig, dm, compat);
-                } else {
-                    r.updateConfiguration(config, dm, compat);
-                }
-                //Slog.i(TAG, "Updated app resources " + v.getKey()
-                //        + " " + r + ": " + r.getConfiguration());
-            } else {
-                //Slog.i(TAG, "Removing old resources " + v.getKey());
-                it.remove();
-            }
-        }
-        
-        return changes != 0;
-    }
-
-    final void applyNonDefaultDisplayMetricsToConfigurationLocked(
-            DisplayMetrics dm, Configuration config) {
-        config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
-        config.densityDpi = dm.densityDpi;
-        config.screenWidthDp = (int)(dm.widthPixels / dm.density);
-        config.screenHeightDp = (int)(dm.heightPixels / dm.density);
-        int sl = Configuration.resetScreenLayout(config.screenLayout);
-        if (dm.widthPixels > dm.heightPixels) {
-            config.orientation = Configuration.ORIENTATION_LANDSCAPE;
-            config.screenLayout = Configuration.reduceScreenLayout(sl,
-                    config.screenWidthDp, config.screenHeightDp);
-        } else {
-            config.orientation = Configuration.ORIENTATION_PORTRAIT;
-            config.screenLayout = Configuration.reduceScreenLayout(sl,
-                    config.screenHeightDp, config.screenWidthDp);
-        }
-        config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate
-        config.compatScreenWidthDp = config.screenWidthDp;
-        config.compatScreenHeightDp = config.screenHeightDp;
-        config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
-    }
-
     final Configuration applyCompatConfiguration(int displayDensity) {
         Configuration config = mConfiguration;
         if (mCompatConfiguration == null) {
             mCompatConfiguration = new Configuration();
         }
         mCompatConfiguration.setTo(mConfiguration);
-        if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
-            mResCompatibilityInfo.applyToConfiguration(displayDensity, mCompatConfiguration);
+        if (mResourcesManager.applyCompatConfiguration(displayDensity, mCompatConfiguration)) {
             config = mCompatConfiguration;
         }
         return config;
@@ -4065,7 +3813,7 @@
 
         int configDiff = 0;
 
-        synchronized (mPackages) {
+        synchronized (mResourcesManager) {
             if (mPendingConfiguration != null) {
                 if (!mPendingConfiguration.isOtherSeqNewer(config)) {
                     config = mPendingConfiguration;
@@ -4081,9 +3829,9 @@
             
             if (DEBUG_CONFIGURATION) Slog.v(TAG, "Handle configuration changed: "
                     + config);
-        
-            applyConfigurationToResourcesLocked(config, compat);
-            
+
+            mResourcesManager.applyConfigurationToResourcesLocked(config, compat);
+
             if (mConfiguration == null) {
                 mConfiguration = new Configuration();
             }
@@ -4333,7 +4081,7 @@
          * reflect configuration changes. The configuration object passed
          * in AppBindData can be safely assumed to be up to date
          */
-        applyConfigurationToResourcesLocked(data.config, data.compatInfo);
+        mResourcesManager.applyConfigurationToResourcesLocked(data.config, data.compatInfo);
         mCurDefaultDisplayDpi = data.config.densityDpi;
         applyCompatConfiguration(mCurDefaultDisplayDpi);
 
@@ -4824,12 +4572,11 @@
                 mProviderRefCountMap.remove(jBinder);
             }
 
-            Iterator<ProviderClientRecord> iter = mProviderMap.values().iterator();
-            while (iter.hasNext()) {
-                ProviderClientRecord pr = iter.next();
+            for (int i=mProviderMap.size()-1; i>=0; i--) {
+                ProviderClientRecord pr = mProviderMap.valueAt(i);
                 IBinder myBinder = pr.mProvider.asBinder();
                 if (myBinder == jBinder) {
-                    iter.remove();
+                    mProviderMap.removeAt(i);
                 }
             }
         }
@@ -5045,6 +4792,7 @@
         mSystemThread = system;
         if (!system) {
             ViewRootImpl.addFirstDrawHandler(new Runnable() {
+                @Override
                 public void run() {
                     ensureJitEnabled();
                 }
@@ -5081,12 +4829,13 @@
         DropBox.setReporter(new DropBoxReporter());
 
         ViewRootImpl.addConfigCallback(new ComponentCallbacks2() {
+            @Override
             public void onConfigurationChanged(Configuration newConfig) {
-                synchronized (mPackages) {
+                synchronized (mResourcesManager) {
                     // We need to apply this change to the resources
                     // immediately, because upon returning the view
                     // hierarchy will be informed about it.
-                    if (applyConfigurationToResourcesLocked(newConfig, null)) {
+                    if (mResourcesManager.applyConfigurationToResourcesLocked(newConfig, null)) {
                         // This actually changed the resources!  Tell
                         // everyone about it.
                         if (mPendingConfiguration == null ||
@@ -5098,8 +4847,10 @@
                     }
                 }
             }
+            @Override
             public void onLowMemory() {
             }
+            @Override
             public void onTrimMemory(int level) {
             }
         });
@@ -5119,12 +4870,11 @@
     }
 
     public int getIntCoreSetting(String key, int defaultValue) {
-        synchronized (mPackages) {
+        synchronized (mResourcesManager) {
             if (mCoreSettings != null) {
                 return mCoreSettings.getInt(key, defaultValue);
-            } else {
-                return defaultValue;
             }
+            return defaultValue;
         }
     }
 
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 2fe682d..d9c3775 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -16,7 +16,9 @@
 
 package android.app;
 
+import android.content.Context;
 import android.content.Intent;
+import android.os.Build;
 import android.os.RemoteException;
 
 /**
@@ -52,6 +54,8 @@
  */
 public class AlarmManager
 {
+    private static final String TAG = "AlarmManager";
+
     /**
      * Alarm time in {@link System#currentTimeMillis System.currentTimeMillis()}
      * (wall clock time in UTC), which will wake up the device when
@@ -80,17 +84,33 @@
      */
     public static final int ELAPSED_REALTIME = 3;
 
+    /** @hide */
+    public static final long WINDOW_EXACT = 0;
+    /** @hide */
+    public static final long WINDOW_HEURISTIC = -1;
+
     private final IAlarmManager mService;
+    private final boolean mAlwaysExact;
+
 
     /**
      * package private on purpose
      */
-    AlarmManager(IAlarmManager service) {
+    AlarmManager(IAlarmManager service, Context ctx) {
         mService = service;
+
+        final int sdkVersion = ctx.getApplicationInfo().targetSdkVersion;
+        mAlwaysExact = (sdkVersion < Build.VERSION_CODES.KEY_LIME_PIE);
     }
-    
+
+    private long legacyExactLength() {
+        return (mAlwaysExact ? WINDOW_EXACT : WINDOW_HEURISTIC);
+    }
+
     /**
-     * Schedule an alarm.  <b>Note: for timing operations (ticks, timeouts,
+     * TBW: discussion of fuzzy nature of alarms in KLP+.
+     *
+     * <p>Schedule an alarm.  <b>Note: for timing operations (ticks, timeouts,
      * etc) it is easier and much more efficient to use
      * {@link android.os.Handler}.</b>  If there is already an alarm scheduled
      * for the same IntentSender, it will first be canceled.
@@ -122,7 +142,9 @@
      * IntentSender.getBroadcast()}.
      *
      * @see android.os.Handler
+     * @see #setExact
      * @see #setRepeating
+     * @see #setWindow
      * @see #cancel
      * @see android.content.Context#sendBroadcast
      * @see android.content.Context#registerReceiver
@@ -133,10 +155,7 @@
      * @see #RTC_WAKEUP
      */
     public void set(int type, long triggerAtMillis, PendingIntent operation) {
-        try {
-            mService.set(type, triggerAtMillis, operation);
-        } catch (RemoteException ex) {
-        }
+        setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation);
     }
 
     /**
@@ -177,6 +196,8 @@
      *
      * @see android.os.Handler
      * @see #set
+     * @see #setExact
+     * @see #setWindow
      * @see #cancel
      * @see android.content.Context#sendBroadcast
      * @see android.content.Context#registerReceiver
@@ -188,22 +209,95 @@
      */
     public void setRepeating(int type, long triggerAtMillis,
             long intervalMillis, PendingIntent operation) {
+        setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, operation);
+    }
+
+    /**
+     * Schedule an alarm to be delivered within a given window of time.
+     *
+     * TBW: clean up these docs
+     *
+     * @param type One of ELAPSED_REALTIME, ELAPSED_REALTIME_WAKEUP, RTC or
+     *        RTC_WAKEUP.
+     * @param windowStartMillis The earliest time, in milliseconds, that the alarm should
+     *        be delivered, expressed in the appropriate clock's units (depending on the alarm
+     *        type).
+     * @param windowLengthMillis The length of the requested delivery window,
+     *        in milliseconds.  The alarm will be delivered no later than this many
+     *        milliseconds after the windowStartMillis time.  Note that this parameter
+     *        is a <i>duration,</i> not the timestamp of the end of the window.
+     * @param operation Action to perform when the alarm goes off;
+     *        typically comes from {@link PendingIntent#getBroadcast
+     *        IntentSender.getBroadcast()}.
+     *
+     * @see #set
+     * @see #setExact
+     * @see #setRepeating
+     * @see #cancel
+     * @see android.content.Context#sendBroadcast
+     * @see android.content.Context#registerReceiver
+     * @see android.content.Intent#filterEquals
+     * @see #ELAPSED_REALTIME
+     * @see #ELAPSED_REALTIME_WAKEUP
+     * @see #RTC
+     * @see #RTC_WAKEUP
+     */
+    public void setWindow(int type, long windowStartMillis, long windowLengthMillis,
+            PendingIntent operation) {
+        setImpl(type, windowStartMillis, windowLengthMillis, 0, operation);
+    }
+
+    /**
+     * TBW: new 'exact' alarm that must be delivered as nearly as possible
+     * to the precise time specified.
+     */
+    public void setExact(int type, long triggerAtMillis, PendingIntent operation) {
+        setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, operation);
+    }
+
+    private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis,
+            PendingIntent operation) {
         try {
-            mService.setRepeating(type, triggerAtMillis, intervalMillis, operation);
+            mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation);
         } catch (RemoteException ex) {
         }
     }
 
     /**
-     * Available inexact recurrence intervals recognized by
-     * {@link #setInexactRepeating(int, long, long, PendingIntent)} 
+     * @deprecated setInexactRepeating() is deprecated; as of API 19 all
+     * repeating alarms are inexact.
      */
+    @Deprecated
     public static final long INTERVAL_FIFTEEN_MINUTES = 15 * 60 * 1000;
+
+    /**
+     * @deprecated setInexactRepeating() is deprecated; as of API 19 all
+     * repeating alarms are inexact.
+     */
+    @Deprecated
     public static final long INTERVAL_HALF_HOUR = 2*INTERVAL_FIFTEEN_MINUTES;
+
+    /**
+     * @deprecated setInexactRepeating() is deprecated; as of API 19 all
+     * repeating alarms are inexact.
+     */
+    @Deprecated
     public static final long INTERVAL_HOUR = 2*INTERVAL_HALF_HOUR;
+
+    /**
+     * @deprecated setInexactRepeating() is deprecated; as of API 19 all
+     * repeating alarms are inexact.
+     */
+    @Deprecated
     public static final long INTERVAL_HALF_DAY = 12*INTERVAL_HOUR;
+
+    /**
+     * @deprecated setInexactRepeating() is deprecated; as of API 19 all
+     * repeating alarms are inexact.
+     */
+    @Deprecated
     public static final long INTERVAL_DAY = 2*INTERVAL_HALF_DAY;
-    
+
     /**
      * Schedule a repeating alarm that has inexact trigger time requirements;
      * for example, an alarm that repeats every hour, but not necessarily at
@@ -236,6 +330,8 @@
      * typically comes from {@link PendingIntent#getBroadcast
      * IntentSender.getBroadcast()}.
      *
+     * @deprecated As of API 19, all repeating alarms are inexact.
+     *
      * @see android.os.Handler
      * @see #set
      * @see #cancel
@@ -252,12 +348,10 @@
      * @see #INTERVAL_HALF_DAY
      * @see #INTERVAL_DAY
      */
+    @Deprecated
     public void setInexactRepeating(int type, long triggerAtMillis,
             long intervalMillis, PendingIntent operation) {
-        try {
-            mService.setInexactRepeating(type, triggerAtMillis, intervalMillis, operation);
-        } catch (RemoteException ex) {
-        }
+        setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, operation);
     }
     
     /**
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 32bb71e..8d47236 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -16,12 +16,13 @@
 
 package android.app;
 
-import android.Manifest;
+import android.os.Binder;
+import android.os.IBinder;
+import android.util.ArrayMap;
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IAppOpsCallback;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
 
 import android.content.Context;
@@ -33,27 +34,30 @@
 /**
  * API for interacting with "application operation" tracking.  Allows you to:
  *
- * - Note when operations are happening, and find out if they are allowed for the current caller.
- * - Disallow specific apps from doing specific operations.
- * - Collect all of the current information about operations that have been executed or are not
- * being allowed.
- * - Monitor for changes in whether an operation is allowed.
+ * <ul>
+ * <li> Note when operations are happening, and find out if they are allowed for the current
+ * caller.</li>
+ * <li> Disallow specific apps from doing specific operations.</li>
+ * <li> Collect all of the current information about operations that have been executed or are not
+ * being allowed.</li>
+ * <li> Monitor for changes in whether an operation is allowed.</li>
+ * </ul>
  *
- * Each operation is identified by a single integer; these integers are a fixed set of
+ * <p>Each operation is identified by a single integer; these integers are a fixed set of
  * operations, enumerated by the OP_* constants.
  *
- * When checking operations, the result is a "mode" integer indicating the current setting
+ * <p></p>When checking operations, the result is a "mode" integer indicating the current setting
  * for the operation under that caller: MODE_ALLOWED, MODE_IGNORED (don't execute the operation but
  * fake its behavior enough so that the caller doesn't crash), MODE_ERRORED (through a
  * SecurityException back to the caller; the normal operation calls will do this for you).
- *
- * @hide
  */
 public class AppOpsManager {
     final Context mContext;
     final IAppOpsService mService;
-    final HashMap<Callback, IAppOpsCallback> mModeWatchers
-            = new HashMap<Callback, IAppOpsCallback>();
+    final ArrayMap<Callback, IAppOpsCallback> mModeWatchers
+            = new ArrayMap<Callback, IAppOpsCallback>();
+
+    static IBinder sToken;
 
     public static final int MODE_ALLOWED = 0;
     public static final int MODE_IGNORED = 1;
@@ -63,50 +67,95 @@
     //  - increment _NUM_OP
     //  - add rows to sOpToSwitch, sOpNames, sOpPerms
     //  - add descriptive strings to Settings/res/values/arrays.xml
+
+    /** No operation specified. */
     public static final int OP_NONE = -1;
+    /** Access to coarse location information. */
     public static final int OP_COARSE_LOCATION = 0;
+    /** Access to fine location information. */
     public static final int OP_FINE_LOCATION = 1;
+    /** Causing GPS to run. */
     public static final int OP_GPS = 2;
-    public static final int OP_VIBRATE = 3;
-    public static final int OP_READ_CONTACTS = 4;
-    public static final int OP_WRITE_CONTACTS = 5;
-    public static final int OP_READ_CALL_LOG = 6;
-    public static final int OP_WRITE_CALL_LOG = 7;
-    public static final int OP_READ_CALENDAR = 8;
-    public static final int OP_WRITE_CALENDAR = 9;
-    public static final int OP_WIFI_SCAN = 10;
-    public static final int OP_POST_NOTIFICATION = 11;
-    public static final int OP_NEIGHBORING_CELLS = 12;
-    public static final int OP_CALL_PHONE = 13;
-    public static final int OP_READ_SMS = 14;
-    public static final int OP_WRITE_SMS = 15;
-    public static final int OP_RECEIVE_SMS = 16;
-    public static final int OP_RECEIVE_EMERGECY_SMS = 17;
-    public static final int OP_RECEIVE_MMS = 18;
-    public static final int OP_RECEIVE_WAP_PUSH = 19;
-    public static final int OP_SEND_SMS = 20;
-    public static final int OP_READ_ICC_SMS = 21;
-    public static final int OP_WRITE_ICC_SMS = 22;
-    public static final int OP_WRITE_SETTINGS = 23;
-    public static final int OP_SYSTEM_ALERT_WINDOW = 24;
-    public static final int OP_ACCESS_NOTIFICATIONS = 25;
-    public static final int OP_CAMERA = 26;
-    public static final int OP_RECORD_AUDIO = 27;
-    public static final int OP_PLAY_AUDIO = 28;
-    public static final int OP_READ_CLIPBOARD = 29;
-    public static final int OP_WRITE_CLIPBOARD = 30;
-    public static final int OP_TAKE_MEDIA_BUTTONS = 31;
-    public static final int OP_TAKE_AUDIO_FOCUS = 32;
-    public static final int OP_AUDIO_MASTER_VOLUME = 33;
-    public static final int OP_AUDIO_VOICE_VOLUME = 34;
-    public static final int OP_AUDIO_RING_VOLUME = 35;
-    public static final int OP_AUDIO_MEDIA_VOLUME = 36;
-    public static final int OP_AUDIO_ALARM_VOLUME = 37;
-    public static final int OP_AUDIO_NOTIFICATION_VOLUME = 38;
-    public static final int OP_AUDIO_BLUETOOTH_VOLUME = 39;
-    public static final int OP_WAKE_LOCK = 40;
     /** @hide */
-    public static final int _NUM_OP = 41;
+    public static final int OP_VIBRATE = 3;
+    /** @hide */
+    public static final int OP_READ_CONTACTS = 4;
+    /** @hide */
+    public static final int OP_WRITE_CONTACTS = 5;
+    /** @hide */
+    public static final int OP_READ_CALL_LOG = 6;
+    /** @hide */
+    public static final int OP_WRITE_CALL_LOG = 7;
+    /** @hide */
+    public static final int OP_READ_CALENDAR = 8;
+    /** @hide */
+    public static final int OP_WRITE_CALENDAR = 9;
+    /** @hide */
+    public static final int OP_WIFI_SCAN = 10;
+    /** @hide */
+    public static final int OP_POST_NOTIFICATION = 11;
+    /** @hide */
+    public static final int OP_NEIGHBORING_CELLS = 12;
+    /** @hide */
+    public static final int OP_CALL_PHONE = 13;
+    /** @hide */
+    public static final int OP_READ_SMS = 14;
+    /** @hide */
+    public static final int OP_WRITE_SMS = 15;
+    /** @hide */
+    public static final int OP_RECEIVE_SMS = 16;
+    /** @hide */
+    public static final int OP_RECEIVE_EMERGECY_SMS = 17;
+    /** @hide */
+    public static final int OP_RECEIVE_MMS = 18;
+    /** @hide */
+    public static final int OP_RECEIVE_WAP_PUSH = 19;
+    /** @hide */
+    public static final int OP_SEND_SMS = 20;
+    /** @hide */
+    public static final int OP_READ_ICC_SMS = 21;
+    /** @hide */
+    public static final int OP_WRITE_ICC_SMS = 22;
+    /** @hide */
+    public static final int OP_WRITE_SETTINGS = 23;
+    /** @hide */
+    public static final int OP_SYSTEM_ALERT_WINDOW = 24;
+    /** @hide */
+    public static final int OP_ACCESS_NOTIFICATIONS = 25;
+    /** @hide */
+    public static final int OP_CAMERA = 26;
+    /** @hide */
+    public static final int OP_RECORD_AUDIO = 27;
+    /** @hide */
+    public static final int OP_PLAY_AUDIO = 28;
+    /** @hide */
+    public static final int OP_READ_CLIPBOARD = 29;
+    /** @hide */
+    public static final int OP_WRITE_CLIPBOARD = 30;
+    /** @hide */
+    public static final int OP_TAKE_MEDIA_BUTTONS = 31;
+    /** @hide */
+    public static final int OP_TAKE_AUDIO_FOCUS = 32;
+    /** @hide */
+    public static final int OP_AUDIO_MASTER_VOLUME = 33;
+    /** @hide */
+    public static final int OP_AUDIO_VOICE_VOLUME = 34;
+    /** @hide */
+    public static final int OP_AUDIO_RING_VOLUME = 35;
+    /** @hide */
+    public static final int OP_AUDIO_MEDIA_VOLUME = 36;
+    /** @hide */
+    public static final int OP_AUDIO_ALARM_VOLUME = 37;
+    /** @hide */
+    public static final int OP_AUDIO_NOTIFICATION_VOLUME = 38;
+    /** @hide */
+    public static final int OP_AUDIO_BLUETOOTH_VOLUME = 39;
+    /** @hide */
+    public static final int OP_WAKE_LOCK = 40;
+    /** Continually monitoring location data. */
+    public static final int OP_MONITOR_LOCATION = 41;
+    /** @hide */
+    public static final int _NUM_OP = 42;
 
     /**
      * This maps each operation to the operation that serves as the
@@ -158,6 +207,7 @@
             OP_AUDIO_NOTIFICATION_VOLUME,
             OP_AUDIO_BLUETOOTH_VOLUME,
             OP_WAKE_LOCK,
+            OP_COARSE_LOCATION,
     };
 
     /**
@@ -206,6 +256,7 @@
             "AUDIO_NOTIFICATION_VOLUME",
             "AUDIO_BLUETOOTH_VOLUME",
             "WAKE_LOCK",
+            "MONITOR_LOCATION",
     };
 
     /**
@@ -254,10 +305,12 @@
             null, // no permission for changing notification volume
             null, // no permission for changing bluetooth volume
             android.Manifest.permission.WAKE_LOCK,
+            null, // no permission for generic location monitoring
     };
 
     /**
      * Retrieve the op switch that controls the given operation.
+     * @hide
      */
     public static int opToSwitch(int op) {
         return sOpToSwitch[op];
@@ -273,6 +326,7 @@
 
     /**
      * Retrieve the permission associated with an operation, or null if there is not one.
+     * @hide
      */
     public static String opToPermission(int op) {
         return sOpPerms[op];
@@ -280,6 +334,7 @@
 
     /**
      * Class holding all of the operation information associated with an app.
+     * @hide
      */
     public static class PackageOps implements Parcelable {
         private final String mPackageName;
@@ -342,6 +397,7 @@
 
     /**
      * Class holding the information about one unique operation of an application.
+     * @hide
      */
     public static class OpEntry implements Parcelable {
         private final int mOp;
@@ -422,7 +478,7 @@
         public void opChanged(int op, String packageName);
     }
 
-    public AppOpsManager(Context context, IAppOpsService service) {
+    AppOpsManager(Context context, IAppOpsService service) {
         mContext = context;
         mService = service;
     }
@@ -431,6 +487,7 @@
      * Retrieve current operation state for all applications.
      *
      * @param ops The set of operations you are interested in, or null if you want all of them.
+     * @hide
      */
     public List<AppOpsManager.PackageOps> getPackagesForOps(int[] ops) {
         try {
@@ -446,6 +503,7 @@
      * @param uid The uid of the application of interest.
      * @param packageName The name of the application of interest.
      * @param ops The set of operations you are interested in, or null if you want all of them.
+     * @hide
      */
     public List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, int[] ops) {
         try {
@@ -455,6 +513,7 @@
         return null;
     }
 
+    /** @hide */
     public void setMode(int code, int uid, String packageName, int mode) {
         try {
             mService.setMode(code, uid, packageName, mode);
@@ -462,6 +521,12 @@
         }
     }
 
+    /**
+     * Monitor for changes to the operating mode for the given op in the given app package.
+     * @param op The operation to monitor, one of OP_*.
+     * @param packageName The name of the application to monitor.
+     * @param callback Where to report changes.
+     */
     public void startWatchingMode(int op, String packageName, final Callback callback) {
         synchronized (mModeWatchers) {
             IAppOpsCallback cb = mModeWatchers.get(callback);
@@ -480,6 +545,10 @@
         }
     }
 
+    /**
+     * Stop monitoring that was previously started with {@link #startWatchingMode}.  All
+     * monitoring associated with this callback will be removed.
+     */
     public void stopWatchingMode(Callback callback) {
         synchronized (mModeWatchers) {
             IAppOpsCallback cb = mModeWatchers.get(callback);
@@ -492,6 +561,22 @@
         }
     }
 
+    /**
+     * Do a quick check for whether an application might be able to perform an operation.
+     * This is <em>not</em> a security check; you must use {@link #noteOp(int, int, String)}
+     * or {@link #startOp(int, int, String)} for your actual security checks, which also
+     * ensure that the given uid and package name are consistent.  This function can just be
+     * used for a quick check to see if an operation has been disabled for the application,
+     * as an early reject of some work.  This does not modify the time stamp or other data
+     * about the operation.
+     * @param op The operation to check.  One of the OP_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     * @throws SecurityException If the app has been configured to crash on this op.
+     */
     public int checkOp(int op, int uid, String packageName) {
         try {
             int mode = mService.checkOperation(op, uid, packageName);
@@ -504,6 +589,10 @@
         return MODE_IGNORED;
     }
 
+    /**
+     * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it
+     * returns {@link #MODE_ERRORED}.
+     */
     public int checkOpNoThrow(int op, int uid, String packageName) {
         try {
             return mService.checkOperation(op, uid, packageName);
@@ -512,6 +601,20 @@
         return MODE_IGNORED;
     }
 
+    /**
+     * Make note of an application performing an operation.  Note that you must pass
+     * in both the uid and name of the application to be checked; this function will verify
+     * that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
+     * succeeds, the last execution time of the operation for this app will be updated to
+     * the current time.
+     * @param op The operation to note.  One of the OP_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     * @throws SecurityException If the app has been configured to crash on this op.
+     */
     public int noteOp(int op, int uid, String packageName) {
         try {
             int mode = mService.noteOperation(op, uid, packageName);
@@ -524,6 +627,10 @@
         return MODE_IGNORED;
     }
 
+    /**
+     * Like {@link #noteOp} but instead of throwing a {@link SecurityException} it
+     * returns {@link #MODE_ERRORED}.
+     */
     public int noteOpNoThrow(int op, int uid, String packageName) {
         try {
             return mService.noteOperation(op, uid, packageName);
@@ -532,13 +639,45 @@
         return MODE_IGNORED;
     }
 
+    /** @hide */
     public int noteOp(int op) {
         return noteOp(op, Process.myUid(), mContext.getBasePackageName());
     }
 
+    /** @hide */
+    public static IBinder getToken(IAppOpsService service) {
+        synchronized (AppOpsManager.class) {
+            if (sToken != null) {
+                return sToken;
+            }
+            try {
+                sToken = service.getToken(new Binder());
+            } catch (RemoteException e) {
+                // System is dead, whatevs.
+            }
+            return sToken;
+        }
+    }
+
+    /**
+     * Report that an application has started executing a long-running operation.  Note that you
+     * must pass in both the uid and name of the application to be checked; this function will
+     * verify that these two match, and if not, return {@link #MODE_IGNORED}.  If this call
+     * succeeds, the last execution time of the operation for this app will be updated to
+     * the current time and the operation will be marked as "running".  In this case you must
+     * later call {@link #finishOp(int, int, String)} to report when the application is no
+     * longer performing the operation.
+     * @param op The operation to start.  One of the OP_* constants.
+     * @param uid The user id of the application attempting to perform the operation.
+     * @param packageName The name of the application attempting to perform the operation.
+     * @return Returns {@link #MODE_ALLOWED} if the operation is allowed, or
+     * {@link #MODE_IGNORED} if it is not allowed and should be silently ignored (without
+     * causing the app to crash).
+     * @throws SecurityException If the app has been configured to crash on this op.
+     */
     public int startOp(int op, int uid, String packageName) {
         try {
-            int mode = mService.startOperation(op, uid, packageName);
+            int mode = mService.startOperation(getToken(mService), op, uid, packageName);
             if (mode == MODE_ERRORED) {
                 throw new SecurityException("Operation not allowed");
             }
@@ -548,21 +687,32 @@
         return MODE_IGNORED;
     }
 
+    /**
+     * Like {@link #startOp} but instead of throwing a {@link SecurityException} it
+     * returns {@link #MODE_ERRORED}.
+     */
     public int startOpNoThrow(int op, int uid, String packageName) {
         try {
-            return mService.startOperation(op, uid, packageName);
+            return mService.startOperation(getToken(mService), op, uid, packageName);
         } catch (RemoteException e) {
         }
         return MODE_IGNORED;
     }
 
+    /** @hide */
     public int startOp(int op) {
         return startOp(op, Process.myUid(), mContext.getBasePackageName());
     }
 
+    /**
+     * Report that an application is no longer performing an operation that had previously
+     * been started with {@link #startOp(int, int, String)}.  There is no validation of input
+     * or result; the parameters supplied here must be the exact same ones previously passed
+     * in when starting the operation.
+     */
     public void finishOp(int op, int uid, String packageName) {
         try {
-            mService.finishOperation(op, uid, packageName);
+            mService.finishOperation(getToken(mService), op, uid, packageName);
         } catch (RemoteException e) {
         }
     }
diff --git a/core/java/android/app/ApplicationLoaders.java b/core/java/android/app/ApplicationLoaders.java
index a26b88c..413c369 100644
--- a/core/java/android/app/ApplicationLoaders.java
+++ b/core/java/android/app/ApplicationLoaders.java
@@ -17,11 +17,9 @@
 package android.app;
 
 import android.os.Trace;
+import android.util.ArrayMap;
 import dalvik.system.PathClassLoader;
 
-import java.util.HashMap;
-import java.util.Map;
-
 class ApplicationLoaders
 {
     public static ApplicationLoaders getDefault()
@@ -71,7 +69,7 @@
         }
     }
 
-    private final Map<String, ClassLoader> mLoaders = new HashMap<String, ClassLoader>();
+    private final ArrayMap<String, ClassLoader> mLoaders = new ArrayMap<String, ClassLoader>();
 
     private static final ApplicationLoaders gApplicationLoaders
         = new ApplicationLoaders();
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 432e9b1..ab2739d 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -51,6 +51,7 @@
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.view.Display;
 
@@ -859,26 +860,20 @@
             boolean needCleanup = false;
             for (String ssp : pkgList) {
                 synchronized (sSync) {
-                    if (sIconCache.size() > 0) {
-                        Iterator<ResourceName> it = sIconCache.keySet().iterator();
-                        while (it.hasNext()) {
-                            ResourceName nm = it.next();
-                            if (nm.packageName.equals(ssp)) {
-                                //Log.i(TAG, "Removing cached drawable for " + nm);
-                                it.remove();
-                                needCleanup = true;
-                            }
+                    for (int i=sIconCache.size()-1; i>=0; i--) {
+                        ResourceName nm = sIconCache.keyAt(i);
+                        if (nm.packageName.equals(ssp)) {
+                            //Log.i(TAG, "Removing cached drawable for " + nm);
+                            sIconCache.removeAt(i);
+                            needCleanup = true;
                         }
                     }
-                    if (sStringCache.size() > 0) {
-                        Iterator<ResourceName> it = sStringCache.keySet().iterator();
-                        while (it.hasNext()) {
-                            ResourceName nm = it.next();
-                            if (nm.packageName.equals(ssp)) {
-                                //Log.i(TAG, "Removing cached string for " + nm);
-                                it.remove();
-                                needCleanup = true;
-                            }
+                    for (int i=sStringCache.size()-1; i>=0; i--) {
+                        ResourceName nm = sStringCache.keyAt(i);
+                        if (nm.packageName.equals(ssp)) {
+                            //Log.i(TAG, "Removing cached string for " + nm);
+                            sStringCache.removeAt(i);
+                            needCleanup = true;
                         }
                     }
                 }
@@ -1335,8 +1330,8 @@
     private final IPackageManager mPM;
 
     private static final Object sSync = new Object();
-    private static HashMap<ResourceName, WeakReference<Drawable.ConstantState>> sIconCache
-            = new HashMap<ResourceName, WeakReference<Drawable.ConstantState>>();
-    private static HashMap<ResourceName, WeakReference<CharSequence>> sStringCache
-            = new HashMap<ResourceName, WeakReference<CharSequence>>();
+    private static ArrayMap<ResourceName, WeakReference<Drawable.ConstantState>> sIconCache
+            = new ArrayMap<ResourceName, WeakReference<Drawable.ConstantState>>();
+    private static ArrayMap<ResourceName, WeakReference<CharSequence>> sStringCache
+            = new ArrayMap<ResourceName, WeakReference<CharSequence>>();
 }
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index e903447..6f18e84 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -111,8 +111,9 @@
         {
             data.enforceInterface(IApplicationThread.descriptor);
             IBinder b = data.readStrongBinder();
+            int procState = data.readInt();
             boolean isForward = data.readInt() != 0;
-            scheduleResumeActivity(b, isForward);
+            scheduleResumeActivity(b, procState, isForward);
             return true;
         }
         
@@ -134,6 +135,7 @@
             ActivityInfo info = ActivityInfo.CREATOR.createFromParcel(data);
             Configuration curConfig = Configuration.CREATOR.createFromParcel(data);
             CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
+            int procState = data.readInt();
             Bundle state = data.readBundle();
             List<ResultInfo> ri = data.createTypedArrayList(ResultInfo.CREATOR);
             List<Intent> pi = data.createTypedArrayList(Intent.CREATOR);
@@ -143,8 +145,8 @@
             ParcelFileDescriptor profileFd = data.readInt() != 0
                     ? data.readFileDescriptor() : null;
             boolean autoStopProfiler = data.readInt() != 0;
-            scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, state, ri, pi,
-                    notResumed, isForward, profileName, profileFd, autoStopProfiler);
+            scheduleLaunchActivity(intent, b, ident, info, curConfig, compatInfo, procState, state,
+                    ri, pi, notResumed, isForward, profileName, profileFd, autoStopProfiler);
             return true;
         }
         
@@ -194,8 +196,9 @@
             Bundle resultExtras = data.readBundle();
             boolean sync = data.readInt() != 0;
             int sendingUser = data.readInt();
+            int processState = data.readInt();
             scheduleReceiver(intent, info, compatInfo, resultCode, resultData,
-                    resultExtras, sync, sendingUser);
+                    resultExtras, sync, sendingUser, processState);
             return true;
         }
 
@@ -204,7 +207,8 @@
             IBinder token = data.readStrongBinder();
             ServiceInfo info = ServiceInfo.CREATOR.createFromParcel(data);
             CompatibilityInfo compatInfo = CompatibilityInfo.CREATOR.createFromParcel(data);
-            scheduleCreateService(token, info, compatInfo);
+            int processState = data.readInt();
+            scheduleCreateService(token, info, compatInfo, processState);
             return true;
         }
 
@@ -213,7 +217,8 @@
             IBinder token = data.readStrongBinder();
             Intent intent = Intent.CREATOR.createFromParcel(data);
             boolean rebind = data.readInt() != 0;
-            scheduleBindService(token, intent, rebind);
+            int processState = data.readInt();
+            scheduleBindService(token, intent, rebind, processState);
             return true;
         }
 
@@ -384,8 +389,9 @@
             boolean ordered = data.readInt() != 0;
             boolean sticky = data.readInt() != 0;
             int sendingUser = data.readInt();
+            int processState = data.readInt();
             scheduleRegisteredReceiver(receiver, intent,
-                    resultCode, dataStr, extras, ordered, sticky, sendingUser);
+                    resultCode, dataStr, extras, ordered, sticky, sendingUser, processState);
             return true;
         }
 
@@ -603,6 +609,25 @@
             reply.writeNoException();
             return true;
         }
+
+        case SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            IBinder token = data.readStrongBinder();
+            boolean timeout = data.readInt() == 1;
+            scheduleTranslucentConversionComplete(token, timeout);
+            reply.writeNoException();
+            return true;
+        }
+
+        case SET_PROCESS_STATE_TRANSACTION:
+        {
+            data.enforceInterface(IApplicationThread.descriptor);
+            int state = data.readInt();
+            setProcessState(state);
+            reply.writeNoException();
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -672,11 +697,12 @@
         data.recycle();
     }
 
-    public final void scheduleResumeActivity(IBinder token, boolean isForward)
+    public final void scheduleResumeActivity(IBinder token, int procState, boolean isForward)
             throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
+        data.writeInt(procState);
         data.writeInt(isForward ? 1 : 0);
         mRemote.transact(SCHEDULE_RESUME_ACTIVITY_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
@@ -696,7 +722,7 @@
 
     public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
             ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-            Bundle state, List<ResultInfo> pendingResults,
+            int procState, Bundle state, List<ResultInfo> pendingResults,
     		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
     		String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
     		throws RemoteException {
@@ -708,6 +734,7 @@
         info.writeToParcel(data, 0);
         curConfig.writeToParcel(data, 0);
         compatInfo.writeToParcel(data, 0);
+        data.writeInt(procState);
         data.writeBundle(state);
         data.writeTypedList(pendingResults);
         data.writeTypedList(pendingNewIntents);
@@ -773,7 +800,7 @@
     
     public final void scheduleReceiver(Intent intent, ActivityInfo info,
             CompatibilityInfo compatInfo, int resultCode, String resultData,
-            Bundle map, boolean sync, int sendingUser) throws RemoteException {
+            Bundle map, boolean sync, int sendingUser, int processState) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         intent.writeToParcel(data, 0);
@@ -784,6 +811,7 @@
         data.writeBundle(map);
         data.writeInt(sync ? 1 : 0);
         data.writeInt(sendingUser);
+        data.writeInt(processState);
         mRemote.transact(SCHEDULE_RECEIVER_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -813,24 +841,26 @@
     }
     
     public final void scheduleCreateService(IBinder token, ServiceInfo info,
-            CompatibilityInfo compatInfo) throws RemoteException {
+            CompatibilityInfo compatInfo, int processState) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
         info.writeToParcel(data, 0);
         compatInfo.writeToParcel(data, 0);
+        data.writeInt(processState);
         mRemote.transact(SCHEDULE_CREATE_SERVICE_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
     }
 
-    public final void scheduleBindService(IBinder token, Intent intent, boolean rebind)
-            throws RemoteException {
+    public final void scheduleBindService(IBinder token, Intent intent, boolean rebind,
+            int processState) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(token);
         intent.writeToParcel(data, 0);
         data.writeInt(rebind ? 1 : 0);
+        data.writeInt(processState);
         mRemote.transact(SCHEDULE_BIND_SERVICE_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -1013,7 +1043,7 @@
 
     public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
             int resultCode, String dataStr, Bundle extras, boolean ordered,
-            boolean sticky, int sendingUser) throws RemoteException {
+            boolean sticky, int sendingUser, int processState) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeStrongBinder(receiver.asBinder());
@@ -1024,6 +1054,7 @@
         data.writeInt(ordered ? 1 : 0);
         data.writeInt(sticky ? 1 : 0);
         data.writeInt(sendingUser);
+        data.writeInt(processState);
         mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -1197,6 +1228,7 @@
         data.recycle();
     }
 
+    @Override
     public void unstableProviderDied(IBinder provider) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
@@ -1205,6 +1237,7 @@
         data.recycle();
     }
 
+    @Override
     public void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType)
             throws RemoteException {
         Parcel data = Parcel.obtain();
@@ -1215,4 +1248,24 @@
         mRemote.transact(REQUEST_ACTIVITY_EXTRAS_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
         data.recycle();
     }
+
+    @Override
+    public void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
+            throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeStrongBinder(token);
+        data.writeInt(timeout ? 1 : 0);
+        mRemote.transact(SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
+
+    @Override
+    public void setProcessState(int state) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeInt(state);
+        mRemote.transact(SET_PROCESS_STATE_TRANSACTION, data, null, IBinder.FLAG_ONEWAY);
+        data.recycle();
+    }
 }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a5106e4..eeee57d 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.os.Build;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.util.Preconditions;
 
@@ -192,6 +193,7 @@
     private Context mReceiverRestrictedContext = null;
     private boolean mRestricted;
     private UserHandle mUser;
+    private ResourcesManager mResourcesManager;
 
     private final Object mSync = new Object();
 
@@ -308,11 +310,11 @@
                     return new ActivityManager(ctx.getOuterContext(), ctx.mMainThread.getHandler());
                 }});
 
-        registerService(ALARM_SERVICE, new StaticServiceFetcher() {
-                public Object createStaticService() {
+        registerService(ALARM_SERVICE, new ServiceFetcher() {
+                public Object createService(ContextImpl ctx) {
                     IBinder b = ServiceManager.getService(ALARM_SERVICE);
                     IAlarmManager service = IAlarmManager.Stub.asInterface(b);
-                    return new AlarmManager(service);
+                    return new AlarmManager(service, ctx);
                 }});
 
         registerService(AUDIO_SERVICE, new ServiceFetcher() {
@@ -705,6 +707,16 @@
                 sSharedPrefs.put(packageName, packagePrefs);
             }
 
+            // At least one application in the world actually passes in a null
+            // name.  This happened to work because when we generated the file name
+            // we would stringify it to "null.xml".  Nice.
+            if (mPackageInfo.getApplicationInfo().targetSdkVersion <
+                    Build.VERSION_CODES.KEY_LIME_PIE) {
+                if (name == null) {
+                    name = "null";
+                }
+            }
+
             sp = packagePrefs.get(name);
             if (sp == null) {
                 File prefsFile = getSharedPrefsFile(name);
@@ -1832,8 +1844,9 @@
 
         ContextImpl c = new ContextImpl();
         c.init(mPackageInfo, null, mMainThread);
-        c.mResources = mMainThread.getTopLevelResources(mPackageInfo.getResDir(), getDisplayId(),
-                overrideConfiguration, mResources.getCompatibilityInfo(), mActivityToken);
+        c.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
+                getDisplayId(), overrideConfiguration, mResources.getCompatibilityInfo(),
+                mActivityToken);
         return c;
     }
 
@@ -1849,8 +1862,8 @@
         context.init(mPackageInfo, null, mMainThread);
         context.mDisplay = display;
         DisplayAdjustments daj = getDisplayAdjustments(displayId);
-        context.mResources = mMainThread.getTopLevelResources(mPackageInfo.getResDir(), displayId,
-                null, daj.getCompatibilityInfo(), null);
+        context.mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
+                displayId, null, daj.getCompatibilityInfo(), null);
         return context;
     }
 
@@ -1929,6 +1942,7 @@
         mPackageInfo = packageInfo;
         mBasePackageName = basePackageName != null ? basePackageName : packageInfo.mPackageName;
         mResources = mPackageInfo.getResources(mainThread);
+        mResourcesManager = ResourcesManager.getInstance();
 
         CompatibilityInfo compatInfo =
                 container == null ? null : container.getCompatibilityInfo();
@@ -1945,7 +1959,7 @@
             }
             mDisplayAdjustments.setCompatibilityInfo(compatInfo);
             mDisplayAdjustments.setActivityToken(activityToken);
-            mResources = mainThread.getTopLevelResources(mPackageInfo.getResDir(),
+            mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
                     Display.DEFAULT_DISPLAY, null, compatInfo, activityToken);
         } else {
             mDisplayAdjustments.setCompatibilityInfo(packageInfo.getCompatibilityInfo());
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index b6aeb84..6933a7a 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -26,6 +26,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AndroidRuntimeException;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.DebugUtils;
 import android.util.Log;
@@ -43,7 +44,6 @@
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.util.HashMap;
 
 final class FragmentState implements Parcelable {
     final String mClassName;
@@ -342,8 +342,8 @@
  * the activity UI was in.
  */
 public class Fragment implements ComponentCallbacks2, OnCreateContextMenuListener {
-    private static final HashMap<String, Class<?>> sClassMap =
-            new HashMap<String, Class<?>>();
+    private static final ArrayMap<String, Class<?>> sClassMap =
+            new ArrayMap<String, Class<?>>();
     
     static final int INVALID_STATE = -1;   // Invalid state used as a null value.
     static final int INITIALIZING = 0;     // Not yet created.
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 3793c73..19858dc 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -258,7 +258,7 @@
             StrictMode.ViolationInfo crashInfo) throws RemoteException;
 
     /*
-     * This will deliver the specified signal to all the persistent processes. Currently only 
+     * This will deliver the specified signal to all the persistent processes. Currently only
      * SIGUSR1 is delivered. All others are ignored.
      */
     public void signalPersistentProcesses(int signal) throws RemoteException;
@@ -301,7 +301,9 @@
 
     public void finishHeavyWeightApp() throws RemoteException;
 
-    public void convertToOpaque(IBinder token) throws RemoteException;
+    public void convertFromTranslucent(IBinder token) throws RemoteException;
+    public void convertToTranslucent(IBinder token) throws RemoteException;
+    public void notifyActivityDrawn(IBinder token) throws RemoteException;
 
     public void setImmersive(IBinder token, boolean immersive) throws RemoteException;
     public boolean isImmersive(IBinder token) throws RemoteException;
@@ -494,7 +496,7 @@
             thisTime = source.readLong();
             totalTime = source.readLong();
         }
-    };
+    }
 
     String descriptor = "android.app.IActivityManager";
 
@@ -670,6 +672,8 @@
     int GET_STACK_BOXES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+170;
     int SET_FOCUSED_STACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+171;
     int GET_STACK_BOX_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+172;
-    int CONVERT_TO_OPAQUE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+173;
-    int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+174;
+    int CONVERT_FROM_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+173;
+    int CONVERT_TO_TRANSLUCENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+174;
+    int NOTIFY_ACTIVITY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+175;
+    int REPORT_ACTIVITY_FULLY_DRAWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+176;
 }
diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl
index edb40ed..0a49ddf 100644
--- a/core/java/android/app/IAlarmManager.aidl
+++ b/core/java/android/app/IAlarmManager.aidl
@@ -24,9 +24,9 @@
  * {@hide}
  */
 interface IAlarmManager {
-    void set(int type, long triggerAtTime, in PendingIntent operation);
-    void setRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
-    void setInexactRepeating(int type, long triggerAtTime, long interval, in PendingIntent operation);
+	/** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */
+    void set(int type, long triggerAtTime, long windowLength,
+            long interval, in PendingIntent operation);
     void setTime(long millis);
     void setTimeZone(String zone);
     void remove(in PendingIntent operation);
diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java
index a009bd3..8e882df 100644
--- a/core/java/android/app/IApplicationThread.java
+++ b/core/java/android/app/IApplicationThread.java
@@ -50,11 +50,12 @@
             int configChanges) throws RemoteException;
     void scheduleWindowVisibility(IBinder token, boolean showWindow) throws RemoteException;
     void scheduleSleeping(IBinder token, boolean sleeping) throws RemoteException;
-    void scheduleResumeActivity(IBinder token, boolean isForward) throws RemoteException;
+    void scheduleResumeActivity(IBinder token, int procState, boolean isForward)
+            throws RemoteException;
     void scheduleSendResult(IBinder token, List<ResultInfo> results) throws RemoteException;
     void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
             ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
-            Bundle state, List<ResultInfo> pendingResults,
+            int procState, Bundle state, List<ResultInfo> pendingResults,
     		List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
     		String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler)
     		throws RemoteException;
@@ -66,7 +67,7 @@
             int configChanges) throws RemoteException;
     void scheduleReceiver(Intent intent, ActivityInfo info, CompatibilityInfo compatInfo,
             int resultCode, String data, Bundle extras, boolean sync,
-            int sendingUser) throws RemoteException;
+            int sendingUser, int processState) throws RemoteException;
     static final int BACKUP_MODE_INCREMENTAL = 0;
     static final int BACKUP_MODE_FULL = 1;
     static final int BACKUP_MODE_RESTORE = 2;
@@ -76,9 +77,9 @@
     void scheduleDestroyBackupAgent(ApplicationInfo app, CompatibilityInfo compatInfo)
             throws RemoteException;
     void scheduleCreateService(IBinder token, ServiceInfo info,
-            CompatibilityInfo compatInfo) throws RemoteException;
+            CompatibilityInfo compatInfo, int processState) throws RemoteException;
     void scheduleBindService(IBinder token,
-            Intent intent, boolean rebind) throws RemoteException;
+            Intent intent, boolean rebind, int processState) throws RemoteException;
     void scheduleUnbindService(IBinder token,
             Intent intent) throws RemoteException;
     void scheduleServiceArgs(IBinder token, boolean taskRemoved, int startId,
@@ -108,7 +109,7 @@
             throws RemoteException;
     void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
             int resultCode, String data, Bundle extras, boolean ordered,
-            boolean sticky, int sendingUser) throws RemoteException;
+            boolean sticky, int sendingUser, int processState) throws RemoteException;
     void scheduleLowMemory() throws RemoteException;
     void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
     void profilerControl(boolean start, String path, ParcelFileDescriptor fd, int profileType)
@@ -133,6 +134,9 @@
     void unstableProviderDied(IBinder provider) throws RemoteException;
     void requestActivityExtras(IBinder activityToken, IBinder requestToken, int requestType)
             throws RemoteException;
+    void scheduleTranslucentConversionComplete(IBinder token, boolean timeout)
+            throws RemoteException;
+    void setProcessState(int state) throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
 
@@ -183,4 +187,6 @@
     int DUMP_DB_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+45;
     int UNSTABLE_PROVIDER_DIED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+46;
     int REQUEST_ACTIVITY_EXTRAS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+47;
+    int SCHEDULE_TRANSLUCENT_CONVERSION_COMPLETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+48;
+    int SET_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+49;
 }
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 573a6aa..4239a5d 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -16,6 +16,7 @@
 
 package android.app;
 
+import android.util.ArrayMap;
 import com.android.internal.util.ArrayUtils;
 
 import android.content.BroadcastReceiver;
@@ -49,8 +50,6 @@
 import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Iterator;
 
 final class IntentReceiverLeaked extends AndroidRuntimeException {
     public IntentReceiverLeaked(String msg) {
@@ -89,14 +88,14 @@
     private ClassLoader mClassLoader;
     private Application mApplication;
 
-    private final HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mReceivers
-        = new HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
-    private final HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
-    = new HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
-    private final HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
-        = new HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
-    private final HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
-        = new HashMap<Context, HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
+    private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
+        = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
+    private final ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
+        = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
+    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
+        = new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
+    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
+        = new ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>>();
 
     int mClientCount = 0;
 
@@ -138,11 +137,11 @@
             if (ActivityThread.mSystemContext == null) {
                 ActivityThread.mSystemContext =
                     ContextImpl.createSystemContext(mainThread);
+                ResourcesManager resourcesManager = ResourcesManager.getInstance();
                 ActivityThread.mSystemContext.getResources().updateConfiguration(
-                         mainThread.getConfiguration(),
-                         mainThread.getDisplayMetricsLocked(
-                                 Display.DEFAULT_DISPLAY, mDisplayAdjustments),
-                         compatInfo);
+                        resourcesManager.getConfiguration(),
+                        resourcesManager.getDisplayMetricsLocked(
+                                 Display.DEFAULT_DISPLAY, mDisplayAdjustments), compatInfo);
                 //Slog.i(TAG, "Created system resources "
                 //        + mSystemContext.getResources() + ": "
                 //        + mSystemContext.getResources().getConfiguration());
@@ -540,12 +539,11 @@
     public void removeContextRegistrations(Context context,
             String who, String what) {
         final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
-        HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
-            mReceivers.remove(context);
+        ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
+                mReceivers.remove(context);
         if (rmap != null) {
-            Iterator<LoadedApk.ReceiverDispatcher> it = rmap.values().iterator();
-            while (it.hasNext()) {
-                LoadedApk.ReceiverDispatcher rd = it.next();
+            for (int i=0; i<rmap.size(); i++) {
+                LoadedApk.ReceiverDispatcher rd = rmap.valueAt(i);
                 IntentReceiverLeaked leak = new IntentReceiverLeaked(
                         what + " " + who + " has leaked IntentReceiver "
                         + rd.getIntentReceiver() + " that was " +
@@ -566,12 +564,11 @@
         }
         mUnregisteredReceivers.remove(context);
         //Slog.i(TAG, "Receiver registrations: " + mReceivers);
-        HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
+        ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
             mServices.remove(context);
         if (smap != null) {
-            Iterator<LoadedApk.ServiceDispatcher> it = smap.values().iterator();
-            while (it.hasNext()) {
-                LoadedApk.ServiceDispatcher sd = it.next();
+            for (int i=0; i<smap.size(); i++) {
+                LoadedApk.ServiceDispatcher sd = smap.valueAt(i);
                 ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
                         what + " " + who + " has leaked ServiceConnection "
                         + sd.getServiceConnection() + " that was originally bound here");
@@ -598,7 +595,7 @@
             Instrumentation instrumentation, boolean registered) {
         synchronized (mReceivers) {
             LoadedApk.ReceiverDispatcher rd = null;
-            HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
+            ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
             if (registered) {
                 map = mReceivers.get(context);
                 if (map != null) {
@@ -610,7 +607,7 @@
                         instrumentation, registered);
                 if (registered) {
                     if (map == null) {
-                        map = new HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
+                        map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                         mReceivers.put(context, map);
                     }
                     map.put(r, rd);
@@ -626,7 +623,7 @@
     public IIntentReceiver forgetReceiverDispatcher(Context context,
             BroadcastReceiver r) {
         synchronized (mReceivers) {
-            HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = mReceivers.get(context);
+            ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = mReceivers.get(context);
             LoadedApk.ReceiverDispatcher rd = null;
             if (map != null) {
                 rd = map.get(r);
@@ -636,10 +633,10 @@
                         mReceivers.remove(context);
                     }
                     if (r.getDebugUnregister()) {
-                        HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
+                        ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
                                 = mUnregisteredReceivers.get(context);
                         if (holder == null) {
-                            holder = new HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
+                            holder = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                             mUnregisteredReceivers.put(context, holder);
                         }
                         RuntimeException ex = new IllegalArgumentException(
@@ -652,7 +649,7 @@
                     return rd.getIIntentReceiver();
                 }
             }
-            HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
+            ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
                     = mUnregisteredReceivers.get(context);
             if (holder != null) {
                 rd = holder.get(r);
@@ -868,14 +865,14 @@
             Context context, Handler handler, int flags) {
         synchronized (mServices) {
             LoadedApk.ServiceDispatcher sd = null;
-            HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
+            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
             if (map != null) {
                 sd = map.get(c);
             }
             if (sd == null) {
                 sd = new ServiceDispatcher(c, context, handler, flags);
                 if (map == null) {
-                    map = new HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
+                    map = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                     mServices.put(context, map);
                 }
                 map.put(c, sd);
@@ -889,7 +886,7 @@
     public final IServiceConnection forgetServiceDispatcher(Context context,
             ServiceConnection c) {
         synchronized (mServices) {
-            HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> map
+            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map
                     = mServices.get(context);
             LoadedApk.ServiceDispatcher sd = null;
             if (map != null) {
@@ -901,10 +898,10 @@
                         mServices.remove(context);
                     }
                     if ((sd.getFlags()&Context.BIND_DEBUG_UNBIND) != 0) {
-                        HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
+                        ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
                                 = mUnboundServices.get(context);
                         if (holder == null) {
-                            holder = new HashMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
+                            holder = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                             mUnboundServices.put(context, holder);
                         }
                         RuntimeException ex = new IllegalArgumentException(
@@ -916,7 +913,7 @@
                     return sd.getIServiceConnection();
                 }
             }
-            HashMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
+            ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
                     = mUnboundServices.get(context);
             if (holder != null) {
                 sd = holder.get(c);
@@ -969,8 +966,8 @@
             }
         }
 
-        private final HashMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
-            = new HashMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
+        private final ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
+            = new ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
 
         ServiceDispatcher(ServiceConnection conn,
                 Context context, Handler activityThread, int flags) {
@@ -1000,9 +997,8 @@
 
         void doForget() {
             synchronized(this) {
-                Iterator<ServiceDispatcher.ConnectionInfo> it = mActiveConnections.values().iterator();
-                while (it.hasNext()) {
-                    ServiceDispatcher.ConnectionInfo ci = it.next();
+                for (int i=0; i<mActiveConnections.size(); i++) {
+                    ServiceDispatcher.ConnectionInfo ci = mActiveConnections.valueAt(i);
                     ci.binder.unlinkToDeath(ci.deathMonitor, 0);
                 }
                 mActiveConnections.clear();
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
new file mode 100644
index 0000000..f55dba4
--- /dev/null
+++ b/core/java/android/app/ResourcesManager.java
@@ -0,0 +1,293 @@
+/*
+ * 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.app;
+
+import static android.app.ActivityThread.DEBUG_CONFIGURATION;
+
+import android.content.pm.ActivityInfo;
+import android.content.res.AssetManager;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.content.res.ResourcesKey;
+import android.hardware.display.DisplayManagerGlobal;
+import android.os.IBinder;
+import android.util.ArrayMap;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Display;
+import android.view.DisplayAdjustments;
+
+import java.lang.ref.WeakReference;
+import java.util.Locale;
+
+/** @hide */
+public class ResourcesManager {
+    static final String TAG = "ResourcesManager";
+    static final boolean DEBUG_CACHE = false;
+    static final boolean DEBUG_STATS = true;
+
+    private static ResourcesManager sResourcesManager;
+    final ArrayMap<ResourcesKey, WeakReference<Resources> > mActiveResources
+            = new ArrayMap<ResourcesKey, WeakReference<Resources> >();
+
+    final ArrayMap<DisplayAdjustments, DisplayMetrics> mDefaultDisplayMetrics
+            = new ArrayMap<DisplayAdjustments, DisplayMetrics>();
+
+    CompatibilityInfo mResCompatibilityInfo;
+
+    Configuration mResConfiguration;
+    final Configuration mTmpConfig = new Configuration();
+
+    public static ResourcesManager getInstance() {
+        synchronized (ResourcesManager.class) {
+            if (sResourcesManager == null) {
+                sResourcesManager = new ResourcesManager();
+            }
+            return sResourcesManager;
+        }
+    }
+
+    public Configuration getConfiguration() {
+        return mResConfiguration;
+    }
+
+    public void flushDisplayMetricsLocked() {
+        mDefaultDisplayMetrics.clear();
+    }
+
+    public DisplayMetrics getDisplayMetricsLocked(int displayId) {
+        return getDisplayMetricsLocked(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
+    }
+
+    public DisplayMetrics getDisplayMetricsLocked(int displayId, DisplayAdjustments daj) {
+        boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+        DisplayMetrics dm = isDefaultDisplay ? mDefaultDisplayMetrics.get(daj) : null;
+        if (dm != null) {
+            return dm;
+        }
+        dm = new DisplayMetrics();
+
+        DisplayManagerGlobal displayManager = DisplayManagerGlobal.getInstance();
+        if (displayManager == null) {
+            // may be null early in system startup
+            dm.setToDefaults();
+            return dm;
+        }
+
+        if (isDefaultDisplay) {
+            mDefaultDisplayMetrics.put(daj, dm);
+        }
+
+        Display d = displayManager.getCompatibleDisplay(displayId, daj);
+        if (d != null) {
+            d.getMetrics(dm);
+        } else {
+            // Display no longer exists
+            // FIXME: This would not be a problem if we kept the Display object around
+            // instead of using the raw display id everywhere.  The Display object caches
+            // its information even after the display has been removed.
+            dm.setToDefaults();
+        }
+        //Slog.i("foo", "New metrics: w=" + metrics.widthPixels + " h="
+        //        + metrics.heightPixels + " den=" + metrics.density
+        //        + " xdpi=" + metrics.xdpi + " ydpi=" + metrics.ydpi);
+        return dm;
+    }
+
+    final void applyNonDefaultDisplayMetricsToConfigurationLocked(
+            DisplayMetrics dm, Configuration config) {
+        config.touchscreen = Configuration.TOUCHSCREEN_NOTOUCH;
+        config.densityDpi = dm.densityDpi;
+        config.screenWidthDp = (int)(dm.widthPixels / dm.density);
+        config.screenHeightDp = (int)(dm.heightPixels / dm.density);
+        int sl = Configuration.resetScreenLayout(config.screenLayout);
+        if (dm.widthPixels > dm.heightPixels) {
+            config.orientation = Configuration.ORIENTATION_LANDSCAPE;
+            config.screenLayout = Configuration.reduceScreenLayout(sl,
+                    config.screenWidthDp, config.screenHeightDp);
+        } else {
+            config.orientation = Configuration.ORIENTATION_PORTRAIT;
+            config.screenLayout = Configuration.reduceScreenLayout(sl,
+                    config.screenHeightDp, config.screenWidthDp);
+        }
+        config.smallestScreenWidthDp = config.screenWidthDp; // assume screen does not rotate
+        config.compatScreenWidthDp = config.screenWidthDp;
+        config.compatScreenHeightDp = config.screenHeightDp;
+        config.compatSmallestScreenWidthDp = config.smallestScreenWidthDp;
+    }
+
+    public boolean applyCompatConfiguration(int displayDensity,
+            Configuration compatConfiguration) {
+        if (mResCompatibilityInfo != null && !mResCompatibilityInfo.supportsScreen()) {
+            mResCompatibilityInfo.applyToConfiguration(displayDensity, compatConfiguration);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Creates the top level Resources for applications with the given compatibility info.
+     *
+     * @param resDir the resource directory.
+     * @param compatInfo the compability info. Must not be null.
+     * @param token the application token for determining stack bounds.
+     */
+    public Resources getTopLevelResources(String resDir, int displayId,
+            Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
+        final float scale = compatInfo.applicationScale;
+        ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale,
+                token);
+        Resources r;
+        synchronized (this) {
+            // Resources is app scale dependent.
+            if (false) {
+                Slog.w(TAG, "getTopLevelResources: " + resDir + " / " + scale);
+            }
+            WeakReference<Resources> wr = mActiveResources.get(key);
+            r = wr != null ? wr.get() : null;
+            //if (r != null) Slog.i(TAG, "isUpToDate " + resDir + ": " + r.getAssets().isUpToDate());
+            if (r != null && r.getAssets().isUpToDate()) {
+                if (false) {
+                    Slog.w(TAG, "Returning cached resources " + r + " " + resDir
+                            + ": appScale=" + r.getCompatibilityInfo().applicationScale);
+                }
+                return r;
+            }
+        }
+
+        //if (r != null) {
+        //    Slog.w(TAG, "Throwing away out-of-date resources!!!! "
+        //            + r + " " + resDir);
+        //}
+
+        AssetManager assets = new AssetManager();
+        if (assets.addAssetPath(resDir) == 0) {
+            return null;
+        }
+
+        //Slog.i(TAG, "Resource: key=" + key + ", display metrics=" + metrics);
+        DisplayMetrics dm = getDisplayMetricsLocked(displayId);
+        Configuration config;
+        boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+        final boolean hasOverrideConfig = key.hasOverrideConfiguration();
+        if (!isDefaultDisplay || hasOverrideConfig) {
+            config = new Configuration(getConfiguration());
+            if (!isDefaultDisplay) {
+                applyNonDefaultDisplayMetricsToConfigurationLocked(dm, config);
+            }
+            if (hasOverrideConfig) {
+                config.updateFrom(key.mOverrideConfiguration);
+            }
+        } else {
+            config = getConfiguration();
+        }
+        r = new Resources(assets, dm, config, compatInfo, token);
+        if (false) {
+            Slog.i(TAG, "Created app resources " + resDir + " " + r + ": "
+                    + r.getConfiguration() + " appScale="
+                    + r.getCompatibilityInfo().applicationScale);
+        }
+
+        synchronized (this) {
+            WeakReference<Resources> wr = mActiveResources.get(key);
+            Resources existing = wr != null ? wr.get() : null;
+            if (existing != null && existing.getAssets().isUpToDate()) {
+                // Someone else already created the resources while we were
+                // unlocked; go ahead and use theirs.
+                r.getAssets().close();
+                return existing;
+            }
+
+            // XXX need to remove entries when weak references go away
+            mActiveResources.put(key, new WeakReference<Resources>(r));
+            return r;
+        }
+    }
+
+    public final boolean applyConfigurationToResourcesLocked(Configuration config,
+            CompatibilityInfo compat) {
+        if (mResConfiguration == null) {
+            mResConfiguration = new Configuration();
+        }
+        if (!mResConfiguration.isOtherSeqNewer(config) && compat == null) {
+            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Skipping new config: curSeq="
+                    + mResConfiguration.seq + ", newSeq=" + config.seq);
+            return false;
+        }
+        int changes = mResConfiguration.updateFrom(config);
+        flushDisplayMetricsLocked();
+        DisplayMetrics defaultDisplayMetrics = getDisplayMetricsLocked(Display.DEFAULT_DISPLAY);
+
+        if (compat != null && (mResCompatibilityInfo == null ||
+                !mResCompatibilityInfo.equals(compat))) {
+            mResCompatibilityInfo = compat;
+            changes |= ActivityInfo.CONFIG_SCREEN_LAYOUT
+                    | ActivityInfo.CONFIG_SCREEN_SIZE
+                    | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+        }
+
+        // set it for java, this also affects newly created Resources
+        if (config.locale != null) {
+            Locale.setDefault(config.locale);
+        }
+
+        Resources.updateSystemConfiguration(config, defaultDisplayMetrics, compat);
+
+        ApplicationPackageManager.configurationChanged();
+        //Slog.i(TAG, "Configuration changed in " + currentPackageName());
+
+        Configuration tmpConfig = null;
+
+        for (int i=mActiveResources.size()-1; i>=0; i--) {
+            ResourcesKey key = mActiveResources.keyAt(i);
+            Resources r = mActiveResources.valueAt(i).get();
+            if (r != null) {
+                if (DEBUG_CONFIGURATION) Slog.v(TAG, "Changing resources "
+                        + r + " config to: " + config);
+                int displayId = key.mDisplayId;
+                boolean isDefaultDisplay = (displayId == Display.DEFAULT_DISPLAY);
+                DisplayMetrics dm = defaultDisplayMetrics;
+                final boolean hasOverrideConfiguration = key.hasOverrideConfiguration();
+                if (!isDefaultDisplay || hasOverrideConfiguration) {
+                    if (tmpConfig == null) {
+                        tmpConfig = new Configuration();
+                    }
+                    tmpConfig.setTo(config);
+                    if (!isDefaultDisplay) {
+                        dm = getDisplayMetricsLocked(displayId);
+                        applyNonDefaultDisplayMetricsToConfigurationLocked(dm, tmpConfig);
+                    }
+                    if (hasOverrideConfiguration) {
+                        tmpConfig.updateFrom(key.mOverrideConfiguration);
+                    }
+                    r.updateConfiguration(tmpConfig, dm, compat);
+                } else {
+                    r.updateConfiguration(config, dm, compat);
+                }
+                //Slog.i(TAG, "Updated app resources " + v.getKey()
+                //        + " " + r + ": " + r.getConfiguration());
+            } else {
+                //Slog.i(TAG, "Removing old resources " + v.getKey());
+                mActiveResources.removeAt(i);
+            }
+        }
+
+        return changes != 0;
+    }
+
+}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index d8bf6ba..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
@@ -306,7 +314,7 @@
     }
 
     /**
-     * Remove a {@link ComponentCallbacks} objec that was previously registered
+     * Remove a {@link ComponentCallbacks} object that was previously registered
      * with {@link #registerComponentCallbacks(ComponentCallbacks)}.
      */
     public void unregisterComponentCallbacks(ComponentCallbacks callback) {
@@ -481,7 +489,7 @@
      * is always on in apps targetting Gingerbread (Android 2.3) and below, and
      * off by default in later versions.
      *
-     * @return Returns the single SharedPreferences instance that can be used
+     * @return The single {@link SharedPreferences} instance that can be used
      *         to retrieve and modify the preference values.
      *
      * @see #MODE_PRIVATE
@@ -499,7 +507,7 @@
      * @param name The name of the file to open; can not contain path
      *             separators.
      *
-     * @return FileInputStream Resulting input stream.
+     * @return The resulting {@link FileInputStream}.
      *
      * @see #openFileOutput
      * @see #fileList
@@ -520,7 +528,7 @@
      * {@link #MODE_WORLD_READABLE} and {@link #MODE_WORLD_WRITEABLE} to control
      * permissions.
      *
-     * @return FileOutputStream Resulting output stream.
+     * @return The resulting {@link FileOutputStream}.
      *
      * @see #MODE_APPEND
      * @see #MODE_PRIVATE
@@ -541,8 +549,8 @@
      * @param name The name of the file to delete; can not contain path
      *             separators.
      *
-     * @return True if the file was successfully deleted; else
-     *         false.
+     * @return {@code true} if the file was successfully deleted; else
+     *         {@code false}.
      *
      * @see #openFileInput
      * @see #openFileOutput
@@ -558,7 +566,7 @@
      * @param name The name of the file for which you would like to get
      *          its path.
      *
-     * @return Returns an absolute path to the given file.
+     * @return An absolute path to the given file.
      *
      * @see #openFileOutput
      * @see #getFilesDir
@@ -570,7 +578,7 @@
      * Returns the absolute path to the directory on the filesystem where
      * files created with {@link #openFileOutput} are stored.
      *
-     * @return Returns the path of the directory holding application files.
+     * @return The path of the directory holding application files.
      *
      * @see #openFileOutput
      * @see #getFileStreamPath
@@ -644,7 +652,7 @@
      * {@link android.os.Environment#DIRECTORY_PICTURES}, or
      * {@link android.os.Environment#DIRECTORY_MOVIES}.
      *
-     * @return Returns the path of the directory holding application files
+     * @return The path of the directory holding application files
      * on external storage.  Returns null if external storage is not currently
      * mounted so it could not ensure the path exists; you will need to call
      * this method again when it is available.
@@ -677,7 +685,7 @@
      * for the amount of space you consume with cache files, and prune those
      * files when exceeding that space.</strong>
      *
-     * @return Returns the path of the directory holding application cache files.
+     * @return The path of the directory holding application cache files.
      *
      * @see #openFileOutput
      * @see #getFileStreamPath
@@ -718,7 +726,7 @@
      * <p>Writing to this path requires the
      * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permission.</p>
      *
-     * @return Returns the path of the directory holding application cache files
+     * @return The path of the directory holding application cache files
      * on external storage.  Returns null if external storage is not currently
      * mounted so it could not ensure the path exists; you will need to call
      * this method again when it is available.
@@ -753,7 +761,7 @@
      * default operation, {@link #MODE_WORLD_READABLE} and
      * {@link #MODE_WORLD_WRITEABLE} to control permissions.
      *
-     * @return Returns a File object for the requested directory.  The directory
+     * @return A {@link File} object for the requested directory.  The directory
      * will have been created if it does not already exist.
      *
      * @see #openFileOutput(String, int)
@@ -819,7 +827,7 @@
      * @param name The name (unique in the application package) of the
      *             database.
      *
-     * @return True if the database was successfully deleted; else false.
+     * @return {@code true} if the database was successfully deleted; else {@code false}.
      *
      * @see #openOrCreateDatabase
      */
@@ -832,7 +840,7 @@
      * @param name The name of the database for which you would like to get
      *          its path.
      *
-     * @return Returns an absolute path to the given database.
+     * @return An absolute path to the given database.
      *
      * @see #openOrCreateDatabase
      */
@@ -910,9 +918,9 @@
      *
      * @param intent The description of the activity to start.
      *
-     * @throws ActivityNotFoundException
+     * @throws ActivityNotFoundException &nbsp;
      *
-     * @see {@link #startActivity(Intent, Bundle)}
+     * @see #startActivity(Intent, Bundle)
      * @see PackageManager#resolveActivity
      */
     public abstract void startActivity(Intent intent);
@@ -924,7 +932,7 @@
      * the INTERACT_ACROSS_USERS_FULL permission.
      * @param intent The description of the activity to start.
      * @param user The UserHandle of the user to start this activity for.
-     * @throws ActivityNotFoundException
+     * @throws ActivityNotFoundException &nbsp;
      * @hide
      */
     public void startActivityAsUser(Intent intent, UserHandle user) {
@@ -951,7 +959,7 @@
      * for how to build the Bundle supplied here; there are no supported definitions
      * for building it manually.
      *
-     * @throws ActivityNotFoundException
+     * @throws ActivityNotFoundException &nbsp;
      *
      * @see #startActivity(Intent)
      * @see PackageManager#resolveActivity
@@ -969,7 +977,7 @@
      * for how to build the Bundle supplied here; there are no supported definitions
      * for building it manually.
      * @param user The UserHandle of the user to start this activity for.
-     * @throws ActivityNotFoundException
+     * @throws ActivityNotFoundException &nbsp;
      * @hide
      */
     public void startActivityAsUser(Intent intent, Bundle options, UserHandle userId) {
@@ -982,9 +990,9 @@
      *
      * @param intents An array of Intents to be started.
      *
-     * @throws ActivityNotFoundException
+     * @throws ActivityNotFoundException &nbsp;
      *
-     * @see {@link #startActivities(Intent[], Bundle)}
+     * @see #startActivities(Intent[], Bundle)
      * @see PackageManager#resolveActivity
      */
     public abstract void startActivities(Intent[] intents);
@@ -1008,9 +1016,9 @@
      * See {@link android.content.Context#startActivity(Intent, Bundle)
      * Context.startActivity(Intent, Bundle)} for more details.
      *
-     * @throws ActivityNotFoundException
+     * @throws ActivityNotFoundException &nbsp;
      *
-     * @see {@link #startActivities(Intent[])}
+     * @see #startActivities(Intent[])
      * @see PackageManager#resolveActivity
      */
     public abstract void startActivities(Intent[] intents, Bundle options);
@@ -1036,9 +1044,9 @@
      * See {@link android.content.Context#startActivity(Intent, Bundle)
      * Context.startActivity(Intent, Bundle)} for more details.
      *
-     * @throws ActivityNotFoundException
+     * @throws ActivityNotFoundException &nbsp;
      *
-     * @see {@link #startActivities(Intent[])}
+     * @see #startActivities(Intent[])
      * @see PackageManager#resolveActivity
      */
     public void startActivitiesAsUser(Intent[] intents, Bundle options, UserHandle userHandle) {
@@ -1604,7 +1612,7 @@
      * {@link ComponentName} of the actual service that was started is
      * returned; else if the service does not exist null is returned.
      *
-     * @throws SecurityException
+     * @throws SecurityException &nbsp;
      *
      * @see #stopService
      * @see #bindService
@@ -1632,9 +1640,9 @@
      *      {@link IntentFilter} published by a service.
      *
      * @return If there is a service matching the given Intent that is already
-     * running, then it is stopped and true is returned; else false is returned.
+     * running, then it is stopped and {@code true} is returned; else {@code false} is returned.
      *
-     * @throws SecurityException
+     * @throws SecurityException &nbsp;
      *
      * @see #startService
      */
@@ -1685,11 +1693,11 @@
      *          {@link #BIND_NOT_FOREGROUND}, {@link #BIND_ABOVE_CLIENT},
      *          {@link #BIND_ALLOW_OOM_MANAGEMENT}, or
      *          {@link #BIND_WAIVE_PRIORITY}.
-     * @return If you have successfully bound to the service, true is returned;
-     *         false is returned if the connection is not made so you will not
+     * @return If you have successfully bound to the service, {@code true} is returned;
+     *         {@code false} is returned if the connection is not made so you will not
      *         receive the service object.
      *
-     * @throws SecurityException
+     * @throws SecurityException &nbsp;
      *
      * @see #unbindService
      * @see #startService
@@ -1741,8 +1749,8 @@
      * @param arguments Additional optional arguments to pass to the
      * instrumentation, or null.
      *
-     * @return Returns true if the instrumentation was successfully started,
-     * else false if it could not be found.
+     * @return {@code true} if the instrumentation was successfully started,
+     * else {@code false} if it could not be found.
      */
     public abstract boolean startInstrumentation(ComponentName className,
             String profileFile, Bundle arguments);
@@ -2217,7 +2225,7 @@
      * and for controlling this device's behavior as a USB device.
      *
      * @see #getSystemService
-     * @see android.harware.usb.UsbManager
+     * @see android.hardware.usb.UsbManager
      */
     public static final String USB_SERVICE = "usb";
 
@@ -2310,7 +2318,7 @@
      * @param uid The user ID being checked against.  A uid of 0 is the root
      * user, which will pass every permission check.
      *
-     * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the given
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the given
      * pid/uid is allowed that permission, or
      * {@link PackageManager#PERMISSION_DENIED} if it is not.
      *
@@ -2332,7 +2340,7 @@
      *
      * @param permission The name of the permission being checked.
      *
-     * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the calling
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the calling
      * pid/uid is allowed that permission, or
      * {@link PackageManager#PERMISSION_DENIED} if it is not.
      *
@@ -2350,7 +2358,7 @@
      *
      * @param permission The name of the permission being checked.
      *
-     * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the calling
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the calling
      * pid/uid is allowed that permission, or
      * {@link PackageManager#PERMISSION_DENIED} if it is not.
      *
@@ -2482,7 +2490,7 @@
      * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or
      * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}.
      *
-     * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the given
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the given
      * pid/uid is allowed to access that uri, or
      * {@link PackageManager#PERMISSION_DENIED} if it is not.
      *
@@ -2505,7 +2513,7 @@
      * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or
      * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}.
      *
-     * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the caller
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the caller
      * is allowed to access that uri, or
      * {@link PackageManager#PERMISSION_DENIED} if it is not.
      *
@@ -2524,7 +2532,7 @@
      * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or
      * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}.
      *
-     * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the caller
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the caller
      * is allowed to access that uri, or
      * {@link PackageManager#PERMISSION_DENIED} if it is not.
      *
@@ -2550,7 +2558,7 @@
      * {@link Intent#FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION} or
      * {@link Intent#FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION}.
      *
-     * @return Returns {@link PackageManager#PERMISSION_GRANTED} if the caller
+     * @return {@link PackageManager#PERMISSION_GRANTED} if the caller
      * is allowed to access that uri or holds one of the given permissions, or
      * {@link PackageManager#PERMISSION_DENIED} if it is not.
      */
@@ -2694,11 +2702,11 @@
      * @param flags Option flags, one of {@link #CONTEXT_INCLUDE_CODE}
      *              or {@link #CONTEXT_IGNORE_SECURITY}.
      *
-     * @return A Context for the application.
+     * @return A {@link Context} for the application.
      *
-     * @throws java.lang.SecurityException
+     * @throws SecurityException &nbsp;
      * @throws PackageManager.NameNotFoundException if there is no application with
-     * the given package name
+     * the given package name.
      */
     public abstract Context createPackageContext(String packageName,
             int flags) throws PackageManager.NameNotFoundException;
@@ -2735,7 +2743,7 @@
      * orientation change), the resources of this context will also change except
      * for those that have been explicitly overridden with a value here.
      *
-     * @return A Context with the given configuration override.
+     * @return A {@link Context} with the given configuration override.
      */
     public abstract Context createConfigurationContext(Configuration overrideConfiguration);
 
@@ -2755,7 +2763,7 @@
      * for whose metrics the Context's resources should be tailored and upon which
      * new windows should be shown.
      *
-     * @return A Context for the display.
+     * @return A {@link Context} for the display.
      */
     public abstract Context createDisplayContext(Display display);
 
@@ -2773,7 +2781,7 @@
     /**
      * Indicates whether this Context is restricted.
      *
-     * @return True if this Context is restricted, false otherwise.
+     * @return {@code true} if this Context is restricted, {@code false} otherwise.
      *
      * @see #CONTEXT_RESTRICTED
      */
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index bda7112..a954f59 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -16,6 +16,7 @@
 
 package android.content;
 
+import android.util.ArraySet;
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
@@ -42,7 +43,6 @@
 import java.io.Serializable;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Locale;
 import java.util.Set;
@@ -1458,7 +1458,7 @@
     /**
      * Broadcast Action: The current time has changed.  Sent every
      * minute.  You can <em>not</em> receive this through components declared
-     * in manifests, only by exlicitly registering for it with
+     * in manifests, only by explicitly registering for it with
      * {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
      * Context.registerReceiver()}.
      *
@@ -2658,7 +2658,7 @@
      * Set if the activity should be an option for the default action
      * (center press) to perform on a piece of data.  Setting this will
      * hide from the user any activities without it set when performing an
-     * action on some data.  Note that this is normal -not- set in the
+     * action on some data.  Note that this is normally -not- set in the
      * Intent when initiating an action -- it is for use in intent filters
      * specified in packages.
      */
@@ -3609,7 +3609,7 @@
     private String mPackage;
     private ComponentName mComponent;
     private int mFlags;
-    private HashSet<String> mCategories;
+    private ArraySet<String> mCategories;
     private Bundle mExtras;
     private Rect mSourceBounds;
     private Intent mSelector;
@@ -3634,7 +3634,7 @@
         this.mComponent = o.mComponent;
         this.mFlags = o.mFlags;
         if (o.mCategories != null) {
-            this.mCategories = new HashSet<String>(o.mCategories);
+            this.mCategories = new ArraySet<String>(o.mCategories);
         }
         if (o.mExtras != null) {
             this.mExtras = new Bundle(o.mExtras);
@@ -3662,7 +3662,7 @@
         this.mPackage = o.mPackage;
         this.mComponent = o.mComponent;
         if (o.mCategories != null) {
-            this.mCategories = new HashSet<String>(o.mCategories);
+            this.mCategories = new ArraySet<String>(o.mCategories);
         }
     }
 
@@ -5226,7 +5226,7 @@
      */
     public Intent addCategory(String category) {
         if (mCategories == null) {
-            mCategories = new HashSet<String>();
+            mCategories = new ArraySet<String>();
         }
         mCategories.add(category.intern());
         return this;
@@ -6370,7 +6370,7 @@
         if (other.mCategories != null
                 && (mCategories == null || (flags&FILL_IN_CATEGORIES) != 0)) {
             if (other.mCategories != null) {
-                mCategories = new HashSet<String>(other.mCategories);
+                mCategories = new ArraySet<String>(other.mCategories);
             }
             changes |= FILL_IN_CATEGORIES;
         }
@@ -6641,12 +6641,9 @@
             }
             first = false;
             b.append("cat=[");
-            Iterator<String> i = mCategories.iterator();
-            boolean didone = false;
-            while (i.hasNext()) {
-                if (didone) b.append(",");
-                didone = true;
-                b.append(i.next());
+            for (int i=0; i<mCategories.size(); i++) {
+                if (i > 0) b.append(',');
+                b.append(mCategories.valueAt(i));
             }
             b.append("]");
         }
@@ -6804,8 +6801,8 @@
             uri.append("action=").append(Uri.encode(mAction)).append(';');
         }
         if (mCategories != null) {
-            for (String category : mCategories) {
-                uri.append("category=").append(Uri.encode(category)).append(';');
+            for (int i=0; i<mCategories.size(); i++) {
+                uri.append("category=").append(Uri.encode(mCategories.valueAt(i))).append(';');
             }
         }
         if (mType != null) {
@@ -6873,9 +6870,10 @@
         }
 
         if (mCategories != null) {
-            out.writeInt(mCategories.size());
-            for (String category : mCategories) {
-                out.writeString(category);
+            final int N = mCategories.size();
+            out.writeInt(N);
+            for (int i=0; i<N; i++) {
+                out.writeString(mCategories.valueAt(i));
             }
         } else {
             out.writeInt(0);
@@ -6927,7 +6925,7 @@
 
         int N = in.readInt();
         if (N > 0) {
-            mCategories = new HashSet<String>();
+            mCategories = new ArraySet<String>();
             int i;
             for (i=0; i<N; i++) {
                 mCategories.add(in.readString().intern());
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8a8751e..81f860e 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -955,6 +955,14 @@
 
     /**
      * Feature for {@link #getSystemAvailableFeatures} and
+     * {@link #hasSystemFeature}: The device supports host-
+     * based NFC card emulation.
+     */
+    @SdkConstant(SdkConstantType.FEATURE)
+    public static final String FEATURE_NFC_HCE = "android.hardware.nfc.hce";
+
+    /**
+     * Feature for {@link #getSystemAvailableFeatures} and
      * {@link #hasSystemFeature}: The device includes an accelerometer.
      */
     @SdkConstant(SdkConstantType.FEATURE)
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 810a521..ce7addc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1554,10 +1554,15 @@
 
         int type;
         PublicKey currentKey = null;
+        int currentKeyDepth = -1;
         Map<PublicKey, Set<String>> definedKeySets = new HashMap<PublicKey, Set<String>>();
         while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
             if (type == XmlPullParser.END_TAG) {
+                if (parser.getDepth() == currentKeyDepth) {
+                    currentKey = null;
+                    currentKeyDepth = -1;
+                }
                 continue;
             }
             String tagname = parser.getName();
@@ -1567,9 +1572,21 @@
                 final String encodedKey = sa.getNonResourceString(
                     com.android.internal.R.styleable.PublicKey_value);
                 currentKey = parsePublicKey(encodedKey);
+                if (currentKey == null) {
+                    Slog.w(TAG, "No valid key in 'publicKey' tag at "
+                            + parser.getPositionDescription());
+                    sa.recycle();
+                    continue;
+                }
+                currentKeyDepth = parser.getDepth();
                 definedKeySets.put(currentKey, new HashSet<String>());
                 sa.recycle();
             } else if (tagname.equals("keyset")) {
+                if (currentKey == null) {
+                    Slog.i(TAG, "'keyset' not in 'publicKey' tag at "
+                            + parser.getPositionDescription());
+                    continue;
+                }
                 final TypedArray sa = res.obtainAttributes(attrs,
                         com.android.internal.R.styleable.KeySet);
                 final String name = sa.getNonResourceString(
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index c24e0ee..6483cd9 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1668,13 +1668,6 @@
     }
 
     /**
-     * @hide
-     */
-    public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) {
-        updateSystemConfiguration(config, metrics, null);
-    }
-    
-    /**
      * Return the current display metrics that are in effect for this resource 
      * object.  The returned object should be treated as read-only.
      * 
diff --git a/core/java/android/content/res/ResourcesKey.java b/core/java/android/content/res/ResourcesKey.java
new file mode 100644
index 0000000..53e0f2c
--- /dev/null
+++ b/core/java/android/content/res/ResourcesKey.java
@@ -0,0 +1,89 @@
+/*
+ * 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.content.res;
+
+import android.os.IBinder;
+
+/** @hide */
+public final class ResourcesKey {
+    final String mResDir;
+    final float mScale;
+    private final int mHash;
+    private final IBinder mToken;
+
+    public final int mDisplayId;
+    public final Configuration mOverrideConfiguration = new Configuration();
+
+    public ResourcesKey(String resDir, int displayId, Configuration overrideConfiguration,
+            float scale, IBinder token) {
+        mResDir = resDir;
+        mDisplayId = displayId;
+        if (overrideConfiguration != null) {
+            mOverrideConfiguration.setTo(overrideConfiguration);
+        }
+        mScale = scale;
+        mToken = token;
+
+        int hash = 17;
+        hash = 31 * hash + (mResDir == null ? 0 : mResDir.hashCode());
+        hash = 31 * hash + mDisplayId;
+        hash = 31 * hash + (mOverrideConfiguration != null
+                ? mOverrideConfiguration.hashCode() : 0);
+        hash = 31 * hash + Float.floatToIntBits(mScale);
+        mHash = hash;
+    }
+
+    public boolean hasOverrideConfiguration() {
+        return !Configuration.EMPTY.equals(mOverrideConfiguration);
+    }
+
+    @Override
+    public int hashCode() {
+        return mHash;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (!(obj instanceof ResourcesKey)) {
+            return false;
+        }
+        ResourcesKey peer = (ResourcesKey) obj;
+        if (!mResDir.equals(peer.mResDir)) {
+            return false;
+        }
+        if (mDisplayId != peer.mDisplayId) {
+            return false;
+        }
+        if (mOverrideConfiguration != peer.mOverrideConfiguration) {
+            if (mOverrideConfiguration == null || peer.mOverrideConfiguration == null) {
+                return false;
+            }
+            if (!mOverrideConfiguration.equals(peer.mOverrideConfiguration)) {
+                return false;
+            }
+        }
+        if (mScale != peer.mScale) {
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return Integer.toHexString(mHash);
+    }
+}
diff --git a/core/java/android/hardware/photography/CameraMetadata.java b/core/java/android/hardware/photography/CameraMetadata.java
index 5488952..c024c05 100644
--- a/core/java/android/hardware/photography/CameraMetadata.java
+++ b/core/java/android/hardware/photography/CameraMetadata.java
@@ -16,11 +16,26 @@
 
 package android.hardware.photography;
 
+import android.hardware.photography.impl.MetadataMarshalClass;
+import android.hardware.photography.impl.MetadataMarshalRect;
+import android.hardware.photography.impl.MetadataMarshalSize;
+import android.hardware.photography.impl.MetadataMarshalString;
 import android.os.Parcelable;
 import android.os.Parcel;
 import android.util.Log;
 
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -72,9 +87,24 @@
      * type to the key.
      */
     public <T> void set(Key<T> key, T value) {
-        Log.e(TAG, "Not fully implemented yet");
+        int tag = key.getTag();
 
-        mMetadataMap.put(key, value);
+        if (value == null) {
+            writeValues(tag, null);
+            return;
+        }
+
+        int nativeType = getNativeType(tag);
+
+        int size = packSingle(value, null, key.mType, nativeType, /* sizeOnly */true);
+
+        // TODO: Optimization. Cache the byte[] and reuse if the size is big enough.
+        byte[] values = new byte[size];
+
+        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
+        packSingle(value, buffer, key.mType, nativeType, /*sizeOnly*/false);
+
+        writeValues(tag, values);
     }
 
     /**
@@ -82,14 +112,565 @@
      * found in {@link CameraProperties}, {@link CaptureResult}, and
      * {@link CaptureRequest}.
      *
+     * @throws IllegalArgumentException if the key was not valid
+     *
      * @param key the metadata field to read.
      * @return the value of that key, or {@code null} if the field is not set.
      */
     @SuppressWarnings("unchecked")
     public <T> T get(Key<T> key) {
-        Log.e(TAG, "Not fully implemented yet");
+        int tag = key.getTag();
+        byte[] values = readValues(tag);
+        if (values == null) {
+            return null;
+        }
 
-        return (T) mMetadataMap.get(key);
+        int nativeType = getNativeType(tag);
+
+        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
+        return unpackSingle(buffer, key.mType, nativeType);
+    }
+
+    // Keep up-to-date with camera_metadata.h
+    /**
+     * @hide
+     */
+    public static final int TYPE_BYTE = 0;
+    /**
+     * @hide
+     */
+    public static final int TYPE_INT32 = 1;
+    /**
+     * @hide
+     */
+    public static final int TYPE_FLOAT = 2;
+    /**
+     * @hide
+     */
+    public static final int TYPE_INT64 = 3;
+    /**
+     * @hide
+     */
+    public static final int TYPE_DOUBLE = 4;
+    /**
+     * @hide
+     */
+    public static final int TYPE_RATIONAL = 5;
+    /**
+     * @hide
+     */
+    public static final int NUM_TYPES = 6;
+
+    private static int getTypeSize(int nativeType) {
+        switch(nativeType) {
+            case TYPE_BYTE:
+                return 1;
+            case TYPE_INT32:
+            case TYPE_FLOAT:
+                return 4;
+            case TYPE_INT64:
+            case TYPE_DOUBLE:
+            case TYPE_RATIONAL:
+                return 8;
+        }
+
+        throw new UnsupportedOperationException("Unknown type, can't get size "
+                + nativeType);
+    }
+
+    private static Class<?> getExpectedType(int nativeType) {
+        switch(nativeType) {
+            case TYPE_BYTE:
+                return Byte.TYPE;
+            case TYPE_INT32:
+                return Integer.TYPE;
+            case TYPE_FLOAT:
+                return Float.TYPE;
+            case TYPE_INT64:
+                return Long.TYPE;
+            case TYPE_DOUBLE:
+                return Double.TYPE;
+            case TYPE_RATIONAL:
+                return Rational.class;
+        }
+
+        throw new UnsupportedOperationException("Unknown type, can't map to Java type "
+                + nativeType);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> int packSingleNative(T value, ByteBuffer buffer, Class<T> type,
+            int nativeType, boolean sizeOnly) {
+
+        if (!sizeOnly) {
+            /**
+             * Rewrite types when the native type doesn't match the managed type
+             *  - Boolean -> Byte
+             *  - Integer -> Byte
+             */
+
+            if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
+                // Since a boolean can't be cast to byte, and we don't want to use putBoolean
+                boolean asBool = (Boolean) value;
+                byte asByte = (byte) (asBool ? 1 : 0);
+                value = (T) (Byte) asByte;
+            } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
+                int asInt = (Integer) value;
+                byte asByte = (byte) asInt;
+                value = (T) (Byte) asByte;
+            } else if (type != getExpectedType(nativeType)) {
+                throw new UnsupportedOperationException("Tried to pack a type of " + type +
+                        " but we expected the type to be " + getExpectedType(nativeType));
+            }
+
+            if (nativeType == TYPE_BYTE) {
+                buffer.put((Byte) value);
+            } else if (nativeType == TYPE_INT32) {
+                buffer.putInt((Integer) value);
+            } else if (nativeType == TYPE_FLOAT) {
+                buffer.putFloat((Float) value);
+            } else if (nativeType == TYPE_INT64) {
+                buffer.putLong((Long) value);
+            } else if (nativeType == TYPE_DOUBLE) {
+                buffer.putDouble((Double) value);
+            } else if (nativeType == TYPE_RATIONAL) {
+                Rational r = (Rational) value;
+                buffer.putInt(r.getNumerator());
+                buffer.putInt(r.getDenominator());
+            }
+
+        }
+
+        return getTypeSize(nativeType);
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    private static <T> int packSingle(T value, ByteBuffer buffer, Class<T> type, int nativeType,
+            boolean sizeOnly) {
+
+        int size = 0;
+
+        if (type.isPrimitive() || type == Rational.class) {
+            size = packSingleNative(value, buffer, type, nativeType, sizeOnly);
+        } else if (type.isEnum()) {
+            size = packEnum((Enum)value, buffer, (Class<Enum>)type, nativeType, sizeOnly);
+        } else if (type.isArray()) {
+            size = packArray(value, buffer, type, nativeType, sizeOnly);
+        } else {
+            size = packClass(value, buffer, type, nativeType, sizeOnly);
+        }
+
+        return size;
+    }
+
+    private static <T extends Enum<T>> int packEnum(T value, ByteBuffer buffer, Class<T> type,
+            int nativeType, boolean sizeOnly) {
+
+        // TODO: add support for enums with their own values.
+        return packSingleNative(getEnumValue(value), buffer, Integer.TYPE, nativeType, sizeOnly);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> int packClass(T value, ByteBuffer buffer, Class<T> type, int nativeType,
+            boolean sizeOnly) {
+
+        MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
+        if (marshaler != null) {
+            return marshaler.marshal(value, buffer, nativeType, sizeOnly);
+        }
+
+        /**
+         * FIXME: This doesn't actually work because getFields() returns fields in an unordered
+         * manner. Although we could sort and get the data to come out correctly on the *java* side,
+         * it would not be data-compatible with our strict XML definitions.
+         *
+         * Rewrite this code to use Parcelables instead, they are most likely compatible with
+         * what we are trying to do in general.
+         */
+        List<Field> instanceFields = findInstanceFields(type);
+        if (instanceFields.size() == 0) {
+            throw new UnsupportedOperationException("Class has no instance fields: " + type);
+        }
+
+        int fieldCount = instanceFields.size();
+        int bufferSize = 0;
+
+        HashSet<Class<?>> fieldTypes = new HashSet<Class<?>>();
+        for (Field f : instanceFields) {
+            fieldTypes.add(f.getType());
+        }
+
+        /**
+         * Pack arguments one field at a time. If we can't access field, look for its accessor
+         * method instead.
+         */
+        for (int i = 0; i < fieldCount; ++i) {
+            Object arg;
+
+            Field f = instanceFields.get(i);
+
+            if ((f.getModifiers() & Modifier.PUBLIC) != 0) {
+                try {
+                    arg = f.get(value);
+                } catch (IllegalAccessException e) {
+                    throw new UnsupportedOperationException(
+                            "Failed to access field " + f + " of type " + type, e);
+                } catch (IllegalArgumentException e) {
+                    throw new UnsupportedOperationException(
+                            "Illegal arguments when accessing field " + f + " of type " + type, e);
+                }
+            } else {
+                Method accessor = null;
+                // try to find a public accessor method
+                for(Method m : type.getMethods()) {
+                    Log.v(TAG, String.format("Looking for getter in method %s for field %s", m, f));
+
+                    // Must have 0 arguments
+                    if (m.getParameterTypes().length != 0) {
+                        continue;
+                    }
+
+                    // Return type must be same as field type
+                    if (m.getReturnType() != f.getType()) {
+                        continue;
+                    }
+
+                    // Strip 'm' from variable prefix if the next letter is capitalized
+                    String fieldName = f.getName();
+                    char[] nameChars = f.getName().toCharArray();
+                    if (nameChars.length >= 2 && nameChars[0] == 'm'
+                            && Character.isUpperCase(nameChars[1])) {
+                        fieldName = String.valueOf(nameChars, /*start*/1, nameChars.length - 1);
+                    }
+
+                    Log.v(TAG, String.format("Normalized field name: %s", fieldName));
+
+                    // #getFoo() , getfoo(), foo(), all match.
+                    if (m.getName().toLowerCase().equals(fieldName.toLowerCase()) ||
+                            m.getName().toLowerCase().equals("get" + fieldName.toLowerCase())) {
+                        accessor = m;
+                        break;
+                    }
+                }
+
+                if (accessor == null) {
+                    throw new UnsupportedOperationException(
+                            "Failed to find getter method for field " + f + " in type " + type);
+                }
+
+                try {
+                    arg = accessor.invoke(value);
+                } catch (IllegalAccessException e) {
+                    // Impossible
+                    throw new UnsupportedOperationException("Failed to access method + " + accessor
+                            + " in type " + type, e);
+                } catch (IllegalArgumentException e) {
+                    // Impossible
+                    throw new UnsupportedOperationException("Bad arguments for method + " + accessor
+                            + " in type " + type, e);
+                } catch (InvocationTargetException e) {
+                    // Possibly but extremely unlikely
+                    throw new UnsupportedOperationException("Failed to invoke method + " + accessor
+                            + " in type " + type, e);
+                }
+            }
+
+            bufferSize += packSingle(arg, buffer, (Class<Object>)f.getType(), nativeType, sizeOnly);
+        }
+
+        return bufferSize;
+    }
+
+    private static <T> int packArray(T value, ByteBuffer buffer, Class<T> type, int nativeType,
+            boolean sizeOnly) {
+
+        int size = 0;
+        int arrayLength = Array.getLength(value);
+
+        @SuppressWarnings("unchecked")
+        Class<Object> componentType = (Class<Object>)type.getComponentType();
+
+        for (int i = 0; i < arrayLength; ++i) {
+            size += packSingle(Array.get(value, i), buffer, componentType, nativeType, sizeOnly);
+        }
+
+        return size;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> T unpackSingleNative(ByteBuffer buffer, Class<T> type, int nativeType) {
+
+        T val;
+
+        if (nativeType == TYPE_BYTE) {
+            val = (T) (Byte) buffer.get();
+        } else if (nativeType == TYPE_INT32) {
+            val = (T) (Integer) buffer.getInt();
+        } else if (nativeType == TYPE_FLOAT) {
+            val = (T) (Float) buffer.getFloat();
+        } else if (nativeType == TYPE_INT64) {
+            val = (T) (Long) buffer.getLong();
+        } else if (nativeType == TYPE_DOUBLE) {
+            val = (T) (Double) buffer.getDouble();
+        } else if (nativeType == TYPE_RATIONAL) {
+            val = (T) new Rational(buffer.getInt(), buffer.getInt());
+        } else {
+            throw new UnsupportedOperationException("Unknown type, can't unpack a native type "
+                + nativeType);
+        }
+
+        /**
+         * Rewrite types when the native type doesn't match the managed type
+         *  - Byte -> Boolean
+         *  - Byte -> Integer
+         */
+
+        if (nativeType == TYPE_BYTE && type == Boolean.TYPE) {
+            // Since a boolean can't be cast to byte, and we don't want to use getBoolean
+            byte asByte = (Byte) val;
+            boolean asBool = asByte != 0;
+            val = (T) (Boolean) asBool;
+        } else if (nativeType == TYPE_BYTE && type == Integer.TYPE) {
+            byte asByte = (Byte) val;
+            int asInt = asByte;
+            val = (T) (Integer) asInt;
+        } else if (type != getExpectedType(nativeType)) {
+            throw new UnsupportedOperationException("Tried to unpack a type of " + type +
+                    " but we expected the type to be " + getExpectedType(nativeType));
+        }
+
+        return val;
+    }
+
+    private static <T> List<Field> findInstanceFields(Class<T> type) {
+        List<Field> fields = new ArrayList<Field>();
+
+        for (Field f : type.getDeclaredFields()) {
+            if (f.isSynthetic()) {
+                throw new UnsupportedOperationException(
+                        "Marshalling synthetic fields not supported in type " + type);
+            }
+
+            // Skip static fields
+            int modifiers = f.getModifiers();
+            if ((modifiers & Modifier.STATIC) == 0) {
+                fields.add(f);
+            }
+
+            Log.v(TAG, String.format("Field %s has modifiers %d", f, modifiers));
+        }
+
+        if (type.getDeclaredFields().length == 0) {
+            Log.w(TAG, String.format("Type %s had 0 fields of any kind", type));
+        }
+        return fields;
+    }
+
+    @SuppressWarnings({"unchecked", "rawtypes"})
+    private static <T> T unpackSingle(ByteBuffer buffer, Class<T> type, int nativeType) {
+
+        if (type.isPrimitive() || type == Rational.class) {
+            return unpackSingleNative(buffer, type, nativeType);
+        }
+
+        if (type.isEnum()) {
+            return (T) unpackEnum(buffer, (Class<Enum>)type, nativeType);
+        }
+
+        if (type.isArray()) {
+            return unpackArray(buffer, type, nativeType);
+        }
+
+        T instance = unpackClass(buffer, type, nativeType);
+
+        return instance;
+    }
+
+    private static <T> Constructor<T> findApplicableConstructor(Class<T> type) {
+
+        List<Field> instanceFields = findInstanceFields(type);
+        if (instanceFields.size() == 0) {
+            throw new UnsupportedOperationException("Class has no instance fields: " + type);
+        }
+
+        Constructor<T> constructor = null;
+        int fieldCount = instanceFields.size();
+
+        HashSet<Class<?>> fieldTypes = new HashSet<Class<?>>();
+        for (Field f : instanceFields) {
+            fieldTypes.add(f.getType());
+        }
+
+        /**
+         * Find which constructor to use:
+         * - must be public
+         * - same amount of arguments as there are instance fields
+         * - each argument is same type as each field (in any order)
+         */
+        @SuppressWarnings("unchecked")
+        Constructor<T>[] constructors = (Constructor<T>[]) type.getConstructors();
+        for (Constructor<T> ctor : constructors) {
+            Log.v(TAG, String.format("Inspecting constructor '%s'", ctor));
+
+            Class<?>[] parameterTypes = ctor.getParameterTypes();
+            if (parameterTypes.length == fieldCount) {
+                boolean match = true;
+
+                HashSet<Class<?>> argTypes = new HashSet<Class<?>>();
+                for (Class<?> t : parameterTypes) {
+                    argTypes.add(t);
+                }
+
+                // Order does not matter
+                match = argTypes.equals(fieldTypes);
+
+                /*
+                // check if the types are the same
+                for (int i = 0; i < fieldCount; ++i) {
+                    if (parameterTypes[i] != instanceFields.get(i).getType()) {
+
+                        Log.v(TAG, String.format(
+                                "Constructor arg (%d) type %s did not match field type %s", i,
+                                parameterTypes[i], instanceFields.get(i).getType()));
+
+                        match = false;
+                        break;
+                    }
+                }
+                */
+
+                if (match) {
+                    constructor = ctor;
+                    break;
+                } else {
+                    Log.w(TAG, String.format("Constructor args did not have matching types"));
+                }
+            } else {
+                Log.v(TAG, String.format(
+                        "Constructor did not have expected amount of fields (had %d, expected %d)",
+                        parameterTypes.length, fieldCount));
+            }
+        }
+
+        if (constructors.length == 0) {
+            Log.w(TAG, String.format("Type %s had no public constructors", type));
+        }
+
+        if (constructor == null) {
+            throw new UnsupportedOperationException(
+                    "Failed to find any applicable constructors for type " + type);
+        }
+
+        return constructor;
+    }
+
+    private static <T extends Enum<T>> T unpackEnum(ByteBuffer buffer, Class<T> type,
+            int nativeType) {
+        int ordinal = unpackSingleNative(buffer, Integer.TYPE, nativeType);
+        return getEnumFromValue(type, ordinal);
+    }
+
+    private static <T> T unpackClass(ByteBuffer buffer, Class<T> type, int nativeType) {
+
+        MetadataMarshalClass<T> marshaler = getMarshaler(type, nativeType);
+        if (marshaler != null) {
+            return marshaler.unmarshal(buffer, nativeType);
+        }
+
+        /**
+         * FIXME: This doesn't actually work because getFields() returns fields in an unordered
+         * manner. Although we could sort and get the data to come out correctly on the *java* side,
+         * it would not be data-compatible with our strict XML definitions.
+         *
+         * Rewrite this code to use Parcelables instead, they are most likely compatible with
+         * what we are trying to do in general.
+         */
+
+        List<Field> instanceFields = findInstanceFields(type);
+        if (instanceFields.size() == 0) {
+            throw new UnsupportedOperationException("Class has no instance fields: " + type);
+        }
+        int fieldCount = instanceFields.size();
+
+        Constructor<T> constructor = findApplicableConstructor(type);
+
+        /**
+         * Build the arguments by unpacking one field at a time
+         * (note that while the field type might be different, the native type is the same)
+         */
+        Object[] arguments = new Object[fieldCount];
+        for (int i = 0; i < fieldCount; ++i) {
+            Object o = unpackSingle(buffer, instanceFields.get(i).getType(), nativeType);
+            arguments[i] = o;
+        }
+
+        T instance;
+        try {
+            instance = constructor.newInstance(arguments);
+        } catch (InstantiationException e) {
+            // type is abstract class, interface, etc...
+            throw new UnsupportedOperationException("Failed to instantiate type " + type, e);
+        } catch (IllegalAccessException e) {
+            // This could happen if we have to access a private.
+            throw new UnsupportedOperationException("Failed to access type " + type, e);
+        } catch (IllegalArgumentException e) {
+            throw new UnsupportedOperationException("Illegal arguments for constructor of type "
+                    + type, e);
+        } catch (InvocationTargetException e) {
+            throw new UnsupportedOperationException(
+                    "Underlying constructor threw exception for type " + type, e);
+        }
+
+        return instance;
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> T unpackArray(ByteBuffer buffer, Class<T> type, int nativeType) {
+
+        Class<?> componentType = type.getComponentType();
+        Object array;
+
+        int elementSize = getTypeSize(nativeType);
+
+        MetadataMarshalClass<?> marshaler = getMarshaler(componentType, nativeType);
+        if (marshaler != null) {
+            elementSize = marshaler.getNativeSize(nativeType);
+        }
+
+        if (elementSize != MetadataMarshalClass.NATIVE_SIZE_DYNAMIC) {
+            int remaining = buffer.remaining();
+            int arraySize = remaining / elementSize;
+
+            Log.v(TAG,
+                    String.format(
+                            "Attempting to unpack array (count = %d, element size = %d, bytes " +
+                                    "remaining = %d) for type %s",
+                            arraySize, elementSize, remaining, type));
+
+            array = Array.newInstance(componentType, arraySize);
+            for (int i = 0; i < arraySize; ++i) {
+               Object elem = unpackSingle(buffer, componentType, nativeType);
+               Array.set(array, i, elem);
+            }
+        } else {
+            // Dynamic size, use an array list.
+            ArrayList<Object> arrayList = new ArrayList<Object>();
+
+            int primitiveSize = getTypeSize(nativeType);
+            while (buffer.remaining() >= primitiveSize) {
+                Object elem = unpackSingle(buffer, componentType, nativeType);
+                arrayList.add(elem);
+            }
+
+            array = arrayList.toArray((T[]) Array.newInstance(componentType, 0));
+        }
+
+        if (buffer.remaining() != 0) {
+            Log.e(TAG, "Trailing bytes (" + buffer.remaining() + ") left over after unpacking "
+                    + type);
+        }
+
+        return (T) array;
     }
 
     @Override
@@ -111,11 +692,22 @@
     }
 
     public static class Key<T> {
-        public Key(String name) {
+
+        private boolean mHasTag;
+        private int mTag;
+        private final Class<T> mType;
+
+        /*
+         * @hide
+         */
+        public Key(String name, Class<T> type) {
             if (name == null) {
                 throw new NullPointerException("Key needs a valid name");
+            } else if (type == null) {
+                throw new NullPointerException("Type needs to be non-null");
             }
             mName = name;
+            mType = type;
         }
 
         public final String getName() {
@@ -144,13 +736,30 @@
         }
 
         private final String mName;
+
+        /**
+         * <p>
+         * Get the tag corresponding to this key. This enables insertion into the
+         * native metadata.
+         * </p>
+         *
+         * <p>This value is looked up the first time, and cached subsequently.</p>
+         *
+         * @return the tag numeric value corresponding to the string
+         *
+         * @hide
+         */
+        public final int getTag() {
+            if (!mHasTag) {
+                mTag = CameraMetadata.getTag(mName);
+                mHasTag = true;
+            }
+            return mTag;
+        }
     }
 
     private final Map<Key<?>, Object> mMetadataMap;
 
-    /**
-     * @hide
-     */
     private long mMetadataPtr; // native CameraMetadata*
 
     private native long nativeAllocate();
@@ -160,6 +769,14 @@
     private native synchronized void nativeClose();
     private native synchronized boolean nativeIsEmpty();
     private native synchronized int nativeGetEntryCount();
+
+    private native synchronized byte[] nativeReadValues(int tag);
+    private native synchronized void nativeWriteValues(int tag, byte[] src);
+
+    private static native int nativeGetTagFromKey(String keyName)
+            throws IllegalArgumentException;
+    private static native int nativeGetTypeFromTag(int tag)
+            throws IllegalArgumentException;
     private static native void nativeClassInit();
 
     /**
@@ -214,6 +831,61 @@
         }
     }
 
+    /**
+     * Convert a key string into the equivalent native tag.
+     *
+     * @throws IllegalArgumentException if the key was not recognized
+     * @throws NullPointerException if the key was null
+     *
+     * @hide
+     */
+    public static int getTag(String key) {
+        return nativeGetTagFromKey(key);
+    }
+
+    /**
+     * Get the underlying native type for a tag.
+     *
+     * @param tag an integer tag, see e.g. {@link #getTag}
+     * @return an int enum for the metadata type, see e.g. {@link #TYPE_BYTE}
+     *
+     * @hide
+     */
+    public static int getNativeType(int tag) {
+        return nativeGetTypeFromTag(tag);
+    }
+
+    /**
+     * <p>Updates the existing entry for tag with the new bytes pointed by src, erasing
+     * the entry if src was null.</p>
+     *
+     * <p>An empty array can be passed in to update the entry to 0 elements.</p>
+     *
+     * @param tag an integer tag, see e.g. {@link #getTag}
+     * @param src an array of bytes, or null to erase the entry
+     *
+     * @hide
+     */
+    public void writeValues(int tag, byte[] src) {
+        nativeWriteValues(tag, src);
+    }
+
+    /**
+     * <p>Returns a byte[] of data corresponding to this tag. Use a wrapped bytebuffer to unserialize
+     * the data properly.</p>
+     *
+     * <p>An empty array can be returned to denote an existing entry with 0 elements.</p>
+     *
+     * @param tag an integer tag, see e.g. {@link #getTag}
+     *
+     * @return null if there were 0 entries for this tag, a byte[] otherwise.
+     * @hide
+     */
+    public byte[] readValues(int tag) {
+     // TODO: Optimization. Native code returns a ByteBuffer instead.
+        return nativeReadValues(tag);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -223,11 +895,120 @@
         }
     }
 
+    private static final HashMap<Class<? extends Enum>, int[]> sEnumValues =
+            new HashMap<Class<? extends Enum>, int[]>();
+    /**
+     * Register a non-sequential set of values to be used with the pack/unpack functions.
+     * This enables get/set to correctly marshal the enum into a value that is C-compatible.
+     *
+     * @param enumType the class for an enum
+     * @param values a list of values mapping to the ordinals of the enum
+     *
+     * @hide
+     */
+    public static <T extends Enum<T>> void registerEnumValues(Class<T> enumType, int[] values) {
+        if (enumType.getEnumConstants().length != values.length) {
+            throw new IllegalArgumentException(
+                    "Expected values array to be the same size as the enumTypes values "
+                            + values.length + " for type " + enumType);
+        }
+
+        Log.v(TAG, "Registered enum values for type " + enumType + " values");
+
+        sEnumValues.put(enumType, values);
+    }
+
+    /**
+     * Get the numeric value from an enum. This is usually the same as the ordinal value for
+     * enums that have fully sequential values, although for C-style enums the range of values
+     * may not map 1:1.
+     *
+     * @param enumValue enum instance
+     * @return int guaranteed to be ABI-compatible with the C enum equivalent
+     */
+    private static <T extends Enum<T>> int getEnumValue(T enumValue) {
+        int[] values;
+        values = sEnumValues.get(enumValue.getClass());
+
+        int ordinal = enumValue.ordinal();
+        if (values != null) {
+            return values[ordinal];
+        }
+
+        return ordinal;
+    }
+
+    /**
+     * Finds the enum corresponding to it's numeric value. Opposite of {@link #getEnumValue} method.
+     *
+     * @param enumType class of the enum we want to find
+     * @param value the numeric value of the enum
+     * @return an instance of the enum
+     */
+    private static <T extends Enum<T>> T getEnumFromValue(Class<T> enumType, int value) {
+        int ordinal;
+
+        int[] registeredValues = sEnumValues.get(enumType);
+        if (registeredValues != null) {
+            ordinal = -1;
+
+            for (int i = 0; i < registeredValues.length; ++i) {
+                if (registeredValues[i] == value) {
+                    ordinal = i;
+                    break;
+                }
+            }
+        } else {
+            ordinal = value;
+        }
+
+        T[] values = enumType.getEnumConstants();
+
+        if (ordinal < 0 || ordinal >= values.length) {
+            throw new IllegalArgumentException(
+                    String.format(
+                            "Argument 'value' (%d) was not a valid enum value for type %s "
+                                    + "(registered? %b)",
+                            value,
+                            enumType, (registeredValues != null)));
+        }
+
+        return values[ordinal];
+    }
+
+    static HashMap<Class<?>, MetadataMarshalClass<?>> sMarshalerMap = new
+            HashMap<Class<?>, MetadataMarshalClass<?>>();
+
+    private static <T> void registerMarshaler(MetadataMarshalClass<T> marshaler) {
+        sMarshalerMap.put(marshaler.getMarshalingClass(), marshaler);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static <T> MetadataMarshalClass<T> getMarshaler(Class<T> type, int nativeType) {
+        MetadataMarshalClass<T> marshaler = (MetadataMarshalClass<T>) sMarshalerMap.get(type);
+
+        if (marshaler != null && !marshaler.isNativeTypeSupported(nativeType)) {
+            throw new UnsupportedOperationException("Unsupported type " + nativeType +
+                    " to be marshalled to/from a " + type);
+        }
+
+        return marshaler;
+    }
+
     /**
      * We use a class initializer to allow the native code to cache some field offsets
      */
     static {
         System.loadLibrary("media_jni");
         nativeClassInit();
+
+        Log.v(TAG, "Shall register metadata marshalers");
+
+        // load built-in marshallers
+        registerMarshaler(new MetadataMarshalRect());
+        registerMarshaler(new MetadataMarshalSize());
+        registerMarshaler(new MetadataMarshalString());
+
+        Log.v(TAG, "Registered metadata marshalers");
     }
-}
\ No newline at end of file
+}
diff --git a/core/java/android/hardware/photography/CameraProperties.java b/core/java/android/hardware/photography/CameraProperties.java
index ad42285..2ed4e3a 100644
--- a/core/java/android/hardware/photography/CameraProperties.java
+++ b/core/java/android/hardware/photography/CameraProperties.java
@@ -40,7 +40,7 @@
      * removable cameras of the same model.
      */
     public static final Key<String> INFO_MODEL =
-            new Key<String>("android.info.model");
+            new Key<String>("android.info.model", String.class);
 
     /**
      * A unique identifier for this camera. For removable cameras, every
@@ -49,7 +49,7 @@
      * identifier is equal to the the device's id.
      */
     public static final Key<String> INFO_IDENTIFIER =
-            new Key<String>("android.info.identifier");
+            new Key<String>("android.info.identifier", String.class);
 
     /**
      * <p>Whether this camera is removable or not.</p>
@@ -59,7 +59,7 @@
      * determine if this camera is a match for a camera device seen earlier.</p>
      */
     public static final Key<Boolean> INFO_REMOVABLE =
-            new Key<Boolean>("android.info.isRemovable");
+            new Key<Boolean>("android.info.isRemovable", Boolean.TYPE);
 
     /**
      * <p>The hardware operational model of this device. One of the
@@ -100,7 +100,7 @@
      * @see #INFO_SUPPORTED_HARDWARE_LEVEL_FULL
      */
     public static final Key<Integer> INFO_SUPPORTED_HARDWARE_LEVEL =
-            new Key<Integer>("android.info.supportedHardwareLevel");
+            new Key<Integer>("android.info.supportedHardwareLevel", Integer.TYPE);
 
     /**
      * <p>The type reported by limited-capability camera devices.</p>
@@ -164,8 +164,8 @@
      * {@link android.graphics.ImageFormat#YUV_420_888} are guaranteed to be
      * supported.</p>
      */
-    public static final Key<Integer[]> SCALER_AVAILABLE_FORMATS =
-            new Key<Integer[]>("android.scaler.availableFormats");
+    public static final Key<int[]> SCALER_AVAILABLE_FORMATS =
+            new Key<int[]>("android.scaler.availableFormats", int[].class);
 
     /**
      * <p>The available output sizes for JPEG buffers from this camera
@@ -174,7 +174,7 @@
      * when using format {@link android.graphics.ImageFormat#JPEG}.</p>
      */
     public static final Key<Size[]> SCALER_AVAILABLE_JPEG_SIZES =
-            new Key<Size[]>("android.scaler.availableJpegSizes");
+            new Key<Size[]>("android.scaler.availableJpegSizes", Size[].class);
 
     /**
      * <p>The available sizes for output buffers from this camera device, when
@@ -194,7 +194,7 @@
      *
      */
     public static final Key<Size[]> SCALER_AVAILABLE_PROCESSED_SIZES =
-            new Key<Size[]>("android.scaler.availableProcessedSizes");
+            new Key<Size[]>("android.scaler.availableProcessedSizes", Size[].class);
 
     /**
      * <p>The available sizes for output buffers from this camera device, when
@@ -207,7 +207,7 @@
      * when using image format {@link android.graphics.ImageFormat#RAW_SENSOR}.</p>
      */
     public static final Key<Size[]> SCALER_AVAILABLE_RAW_SIZES =
-            new Key<Size[]>("android.scaler.availableRawSizes");
+            new Key<Size[]>("android.scaler.availableRawSizes", Size[].class);
 
     /**
      * <p>The coordinates of the sensor's active pixel array, relative to its
@@ -230,7 +230,7 @@
      * being the top-left corner of the active array.</p>
      */
     public static final Key<Rect> SENSOR_ACTIVE_ARRAY_SIZE =
-            new Key<Rect>("android.sensor.activeArraySize");
+            new Key<Rect>("android.sensor.activeArraySize", Rect.class);
 
     /**
      * <p>The size of the sensor's total pixel array available for readout. Some
@@ -240,7 +240,7 @@
      * the supported capture sizes.</p>
      */
     public static final Key<Size> SENSOR_PIXEL_ARRAY_SIZE =
-            new Key<Size>("android.sensor.activeArraySize");
+            new Key<Size>("android.sensor.activeArraySize", Size.class);
 
     // TODO: Many more of these.
 
diff --git a/core/java/android/hardware/photography/CameraPropertiesKeys.java b/core/java/android/hardware/photography/CameraPropertiesKeys.java
new file mode 100644
index 0000000..db8ab44
--- /dev/null
+++ b/core/java/android/hardware/photography/CameraPropertiesKeys.java
@@ -0,0 +1,268 @@
+/*
+ * 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.hardware.photography;
+
+import static android.hardware.photography.CameraMetadata.Key;
+
+/**
+ * ! Do not edit this file directly !
+ *
+ * Generated automatically from CameraPropertiesKeys.mako
+ *
+ * TODO: Include a hash of the input files here that the build can check.
+ */
+
+/**
+ * The base class for camera controls and information.
+ *
+ * This class defines the basic key/value map used for querying for camera
+ * characteristics or capture results, and for setting camera request
+ * parameters.
+ *
+ * @see CameraProperties
+ * @see CameraMetadata
+ * @hide
+ **/
+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<int[]> AE_AVAILABLE_TARGET_FPS_RANGES =
+                new Key<int[]>("android.control.aeAvailableTargetFpsRanges", int[].class);
+        public static final Key<int[]> AE_COMPENSATION_RANGE =
+                new Key<int[]>("android.control.aeCompensationRange", int[].class);
+        public static final Key<Rational> AE_COMPENSATION_STEP =
+                new Key<Rational>("android.control.aeCompensationStep", Rational.class);
+        public static final Key<byte[]> AF_AVAILABLE_MODES =
+                new Key<byte[]>("android.control.afAvailableModes", byte[].class);
+        public static final Key<byte[]> AVAILABLE_EFFECTS =
+                new Key<byte[]>("android.control.availableEffects", byte[].class);
+        public static final Key<byte[]> AVAILABLE_SCENE_MODES =
+                new Key<byte[]>("android.control.availableSceneModes", byte[].class);
+        public static final Key<byte[]> AVAILABLE_VIDEO_STABILIZATION_MODES =
+                new Key<byte[]>("android.control.availableVideoStabilizationModes", byte[].class);
+        public static final Key<byte[]> AWB_AVAILABLE_MODES =
+                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 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 class Jpeg {
+        public static final Key<android.hardware.photography.Size[]> AVAILABLE_THUMBNAIL_SIZES =
+                new Key<android.hardware.photography.Size[]>("android.jpeg.availableThumbnailSizes", android.hardware.photography.Size[].class);
+
+    }
+
+    public static final class Lens {
+        public static final class Info {
+            public static final Key<float[]> AVAILABLE_APERTURES =
+                    new Key<float[]>("android.lens.info.availableApertures", float[].class);
+            public static final Key<float[]> AVAILABLE_FILTER_DENSITIES =
+                    new Key<float[]>("android.lens.info.availableFilterDensities", float[].class);
+            public static final Key<float[]> AVAILABLE_FOCAL_LENGTHS =
+                    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> 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<android.hardware.photography.Size> SHADING_MAP_SIZE =
+                    new Key<android.hardware.photography.Size>("android.lens.info.shadingMapSize", android.hardware.photography.Size.class);
+        }
+
+            public static final class FacingKey extends Key<Lens.FacingKey.Enum> {
+                public enum Enum {
+                    FRONT,
+                    BACK;
+                }
+
+                public static final Enum FRONT = Enum.FRONT;
+                public static final Enum BACK = Enum.BACK;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private FacingKey(String name) {
+                    super(name, Lens.FacingKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Lens.FacingKey.Enum> FACING =
+                new FacingKey("android.lens.facing");
+
+    }
+
+    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 class Scaler {
+
+            public static final class AvailableFormatsKey extends Key<Scaler.AvailableFormatsKey.Enum[]> {
+                public enum Enum {
+                    RAW_SENSOR,
+                    YV12,
+                    YCrCb_420_SP,
+                    IMPLEMENTATION_DEFINED,
+                    YCbCr_420_888,
+                    BLOB;
+                }
+
+                public static final Enum RAW_SENSOR = Enum.RAW_SENSOR;
+                public static final Enum YV12 = Enum.YV12;
+                public static final Enum YCrCb_420_SP = Enum.YCrCb_420_SP;
+                public static final Enum IMPLEMENTATION_DEFINED = Enum.IMPLEMENTATION_DEFINED;
+                public static final Enum YCbCr_420_888 = Enum.YCbCr_420_888;
+                public static final Enum BLOB = Enum.BLOB;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AvailableFormatsKey(String name) {
+                    super(name, Scaler.AvailableFormatsKey.Enum[].class);
+                }
+
+                static {
+                    CameraMetadata.registerEnumValues(Scaler.AvailableFormatsKey.Enum.class, new int[] {
+                        0x20,  // RAW_SENSOR
+                        0x32315659,  // YV12
+                        0x11,  // YCrCb_420_SP
+                        0x22,  // IMPLEMENTATION_DEFINED
+                        0x23,  // YCbCr_420_888
+                        0x21  // BLOB
+                    });
+                }
+            }
+
+        public static final Key<Scaler.AvailableFormatsKey.Enum[]> AVAILABLE_FORMATS =
+                new AvailableFormatsKey("android.scaler.availableFormats");
+        public static final Key<long[]> AVAILABLE_JPEG_MIN_DURATIONS =
+                new Key<long[]>("android.scaler.availableJpegMinDurations", long[].class);
+        public static final Key<android.hardware.photography.Size[]> AVAILABLE_JPEG_SIZES =
+                new Key<android.hardware.photography.Size[]>("android.scaler.availableJpegSizes", android.hardware.photography.Size[].class);
+        public static final Key<Float> AVAILABLE_MAX_DIGITAL_ZOOM =
+                new Key<Float>("android.scaler.availableMaxDigitalZoom", float.class);
+        public static final Key<long[]> AVAILABLE_PROCESSED_MIN_DURATIONS =
+                new Key<long[]>("android.scaler.availableProcessedMinDurations", long[].class);
+        public static final Key<android.hardware.photography.Size[]> AVAILABLE_PROCESSED_SIZES =
+                new Key<android.hardware.photography.Size[]>("android.scaler.availableProcessedSizes", android.hardware.photography.Size[].class);
+
+    }
+
+    public static final class Sensor {
+        public static final class Info {
+            public static final Key<android.graphics.Rect> ACTIVE_ARRAY_SIZE =
+                    new Key<android.graphics.Rect>("android.sensor.info.activeArraySize", android.graphics.Rect.class);
+            public static final Key<int[]> SENSITIVITY_RANGE =
+                    new Key<int[]>("android.sensor.info.sensitivityRange", int[].class);
+            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<android.hardware.photography.Size> PHYSICAL_SIZE =
+                    new Key<android.hardware.photography.Size>("android.sensor.info.physicalSize", android.hardware.photography.Size.class);
+        }
+        public static final Key<Rational> BASE_GAIN_FACTOR =
+                new Key<Rational>("android.sensor.baseGainFactor", Rational.class);
+        public static final Key<Integer> MAX_ANALOG_SENSITIVITY =
+                new Key<Integer>("android.sensor.maxAnalogSensitivity", int.class);
+        public static final Key<Integer> ORIENTATION =
+                new Key<Integer>("android.sensor.orientation", int.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> MAX_FACE_COUNT =
+                    new Key<Integer>("android.statistics.info.maxFaceCount", int.class);
+        }
+
+    }
+
+    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;
+                }
+
+                public static final Enum TRANSMIT = Enum.TRANSMIT;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AvailableLedsKey(String name) {
+                    super(name, Led.AvailableLedsKey.Enum[].class);
+                }
+
+            }
+
+        /**
+         * @hide
+         */
+        public static final Key<Led.AvailableLedsKey.Enum[]> AVAILABLE_LEDS =
+                new AvailableLedsKey("android.led.availableLeds");
+
+    }
+
+    public static final class Info {
+
+            public static final class SupportedHardwareLevelKey extends Key<Info.SupportedHardwareLevelKey.Enum> {
+                public enum Enum {
+                    LIMITED,
+                    FULL;
+                }
+
+                public static final Enum LIMITED = Enum.LIMITED;
+                public static final Enum FULL = Enum.FULL;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private SupportedHardwareLevelKey(String name) {
+                    super(name, Info.SupportedHardwareLevelKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Info.SupportedHardwareLevelKey.Enum> SUPPORTED_HARDWARE_LEVEL =
+                new SupportedHardwareLevelKey("android.info.supportedHardwareLevel");
+
+    }
+
+}
+
+
diff --git a/core/java/android/hardware/photography/CaptureRequest.java b/core/java/android/hardware/photography/CaptureRequest.java
index ac2041b..d4a7a3c 100644
--- a/core/java/android/hardware/photography/CaptureRequest.java
+++ b/core/java/android/hardware/photography/CaptureRequest.java
@@ -58,14 +58,14 @@
      * The exposure time for this capture, in nanoseconds.
      */
     public static final Key<Long> SENSOR_EXPOSURE_TIME =
-            new Key<Long>("android.sensor.exposureTime");
+            new Key<Long>("android.sensor.exposureTime", Long.TYPE);
 
     /**
      * The sensor sensitivity (gain) setting for this camera.
      * This is represented as an ISO sensitivity value
      */
     public static final Key<Integer> SENSOR_SENSITIVITY =
-            new Key<Integer>("android.sensor.sensitivity");
+            new Key<Integer>("android.sensor.sensitivity", Integer.TYPE);
 
     // Many more settings
 
diff --git a/core/java/android/hardware/photography/CaptureRequestKeys.java b/core/java/android/hardware/photography/CaptureRequestKeys.java
new file mode 100644
index 0000000..ca6d487
--- /dev/null
+++ b/core/java/android/hardware/photography/CaptureRequestKeys.java
@@ -0,0 +1,617 @@
+/*
+ * 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.hardware.photography;
+
+import static android.hardware.photography.CameraMetadata.Key;
+
+/**
+ * ! Do not edit this file directly !
+ *
+ * Generated automatically from CaptureRequestKeys.mako
+ *
+ * TODO: Include a hash of the input files here that the build can check.
+ */
+
+/**
+ * The base class for camera controls and information.
+ *
+ * This class defines the basic key/value map used for querying for camera
+ * characteristics or capture results, and for setting camera request
+ * parameters.
+ *
+ * @see CaptureRequest
+ * @see CameraMetadata
+ * @hide
+ **/
+public final class CaptureRequestKeys {
+    public static final class ColorCorrection {
+
+            public static final class ModeKey extends Key<ColorCorrection.ModeKey.Enum> {
+                public enum Enum {
+                    TRANSFORM_MATRIX,
+                    FAST,
+                    HIGH_QUALITY;
+                }
+
+                public static final Enum TRANSFORM_MATRIX = Enum.TRANSFORM_MATRIX;
+                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, ColorCorrection.ModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<ColorCorrection.ModeKey.Enum> MODE =
+                new ModeKey("android.colorCorrection.mode");
+        public static final Key<Rational[]> TRANSFORM =
+                new Key<Rational[]>("android.colorCorrection.transform", Rational[].class);
+        public static final Key<float[]> GAINS =
+                new Key<float[]>("android.colorCorrection.gains", float[].class);
+
+    }
+
+    public static final class Control {
+
+            public static final class AeAntibandingModeKey extends Key<Control.AeAntibandingModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    _50HZ,
+                    _60HZ,
+                    AUTO;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum _50HZ = Enum._50HZ;
+                public static final Enum _60HZ = Enum._60HZ;
+                public static final Enum AUTO = Enum.AUTO;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AeAntibandingModeKey(String name) {
+                    super(name, Control.AeAntibandingModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.AeAntibandingModeKey.Enum> AE_ANTIBANDING_MODE =
+                new AeAntibandingModeKey("android.control.aeAntibandingMode");
+        public static final Key<Integer> AE_EXPOSURE_COMPENSATION =
+                new Key<Integer>("android.control.aeExposureCompensation", int.class);
+        public static final Key<Boolean> AE_LOCK =
+                new Key<Boolean>("android.control.aeLock", boolean.class);
+
+            public static final class AeModeKey extends Key<Control.AeModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    ON,
+                    ON_AUTO_FLASH,
+                    ON_ALWAYS_FLASH,
+                    ON_AUTO_FLASH_REDEYE;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum ON = Enum.ON;
+                public static final Enum ON_AUTO_FLASH = Enum.ON_AUTO_FLASH;
+                public static final Enum ON_ALWAYS_FLASH = Enum.ON_ALWAYS_FLASH;
+                public static final Enum ON_AUTO_FLASH_REDEYE = Enum.ON_AUTO_FLASH_REDEYE;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AeModeKey(String name) {
+                    super(name, Control.AeModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.AeModeKey.Enum> AE_MODE =
+                new AeModeKey("android.control.aeMode");
+        public static final Key<int[]> AE_REGIONS =
+                new Key<int[]>("android.control.aeRegions", int[].class);
+        public static final Key<int[]> AE_TARGET_FPS_RANGE =
+                new Key<int[]>("android.control.aeTargetFpsRange", int[].class);
+
+            public static final class AePrecaptureTriggerKey extends Key<Control.AePrecaptureTriggerKey.Enum> {
+                public enum Enum {
+                    IDLE,
+                    START;
+                }
+
+                public static final Enum IDLE = Enum.IDLE;
+                public static final Enum START = Enum.START;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AePrecaptureTriggerKey(String name) {
+                    super(name, Control.AePrecaptureTriggerKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.AePrecaptureTriggerKey.Enum> AE_PRECAPTURE_TRIGGER =
+                new AePrecaptureTriggerKey("android.control.aePrecaptureTrigger");
+
+            public static final class AfModeKey extends Key<Control.AfModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    AUTO,
+                    MACRO,
+                    CONTINUOUS_VIDEO,
+                    CONTINUOUS_PICTURE,
+                    EDOF;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum AUTO = Enum.AUTO;
+                public static final Enum MACRO = Enum.MACRO;
+                public static final Enum CONTINUOUS_VIDEO = Enum.CONTINUOUS_VIDEO;
+                public static final Enum CONTINUOUS_PICTURE = Enum.CONTINUOUS_PICTURE;
+                public static final Enum EDOF = Enum.EDOF;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AfModeKey(String name) {
+                    super(name, Control.AfModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.AfModeKey.Enum> AF_MODE =
+                new AfModeKey("android.control.afMode");
+        public static final Key<int[]> AF_REGIONS =
+                new Key<int[]>("android.control.afRegions", int[].class);
+
+            public static final class AfTriggerKey extends Key<Control.AfTriggerKey.Enum> {
+                public enum Enum {
+                    IDLE,
+                    START,
+                    CANCEL;
+                }
+
+                public static final Enum IDLE = Enum.IDLE;
+                public static final Enum START = Enum.START;
+                public static final Enum CANCEL = Enum.CANCEL;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AfTriggerKey(String name) {
+                    super(name, Control.AfTriggerKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.AfTriggerKey.Enum> AF_TRIGGER =
+                new AfTriggerKey("android.control.afTrigger");
+        public static final Key<Boolean> AWB_LOCK =
+                new Key<Boolean>("android.control.awbLock", boolean.class);
+
+            public static final class AwbModeKey extends Key<Control.AwbModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    AUTO,
+                    INCANDESCENT,
+                    FLUORESCENT,
+                    WARM_FLUORESCENT,
+                    DAYLIGHT,
+                    CLOUDY_DAYLIGHT,
+                    TWILIGHT,
+                    SHADE;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum AUTO = Enum.AUTO;
+                public static final Enum INCANDESCENT = Enum.INCANDESCENT;
+                public static final Enum FLUORESCENT = Enum.FLUORESCENT;
+                public static final Enum WARM_FLUORESCENT = Enum.WARM_FLUORESCENT;
+                public static final Enum DAYLIGHT = Enum.DAYLIGHT;
+                public static final Enum CLOUDY_DAYLIGHT = Enum.CLOUDY_DAYLIGHT;
+                public static final Enum TWILIGHT = Enum.TWILIGHT;
+                public static final Enum SHADE = Enum.SHADE;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AwbModeKey(String name) {
+                    super(name, Control.AwbModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.AwbModeKey.Enum> AWB_MODE =
+                new AwbModeKey("android.control.awbMode");
+        public static final Key<int[]> AWB_REGIONS =
+                new Key<int[]>("android.control.awbRegions", int[].class);
+
+            public static final class CaptureIntentKey extends Key<Control.CaptureIntentKey.Enum> {
+                public enum Enum {
+                    CUSTOM,
+                    PREVIEW,
+                    STILL_CAPTURE,
+                    VIDEO_RECORD,
+                    VIDEO_SNAPSHOT,
+                    ZERO_SHUTTER_LAG;
+                }
+
+                public static final Enum CUSTOM = Enum.CUSTOM;
+                public static final Enum PREVIEW = Enum.PREVIEW;
+                public static final Enum STILL_CAPTURE = Enum.STILL_CAPTURE;
+                public static final Enum VIDEO_RECORD = Enum.VIDEO_RECORD;
+                public static final Enum VIDEO_SNAPSHOT = Enum.VIDEO_SNAPSHOT;
+                public static final Enum ZERO_SHUTTER_LAG = Enum.ZERO_SHUTTER_LAG;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private CaptureIntentKey(String name) {
+                    super(name, Control.CaptureIntentKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.CaptureIntentKey.Enum> CAPTURE_INTENT =
+                new CaptureIntentKey("android.control.captureIntent");
+
+            public static final class EffectModeKey extends Key<Control.EffectModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    MONO,
+                    NEGATIVE,
+                    SOLARIZE,
+                    SEPIA,
+                    POSTERIZE,
+                    WHITEBOARD,
+                    BLACKBOARD,
+                    AQUA;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum MONO = Enum.MONO;
+                public static final Enum NEGATIVE = Enum.NEGATIVE;
+                public static final Enum SOLARIZE = Enum.SOLARIZE;
+                public static final Enum SEPIA = Enum.SEPIA;
+                public static final Enum POSTERIZE = Enum.POSTERIZE;
+                public static final Enum WHITEBOARD = Enum.WHITEBOARD;
+                public static final Enum BLACKBOARD = Enum.BLACKBOARD;
+                public static final Enum AQUA = Enum.AQUA;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private EffectModeKey(String name) {
+                    super(name, Control.EffectModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.EffectModeKey.Enum> EFFECT_MODE =
+                new EffectModeKey("android.control.effectMode");
+
+            public static final class ModeKey extends Key<Control.ModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    AUTO,
+                    USE_SCENE_MODE;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum AUTO = Enum.AUTO;
+                public static final Enum USE_SCENE_MODE = Enum.USE_SCENE_MODE;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private ModeKey(String name) {
+                    super(name, Control.ModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.ModeKey.Enum> MODE =
+                new ModeKey("android.control.mode");
+
+            public static final class SceneModeKey extends Key<Control.SceneModeKey.Enum> {
+                public enum Enum {
+                    UNSUPPORTED,
+                    FACE_PRIORITY,
+                    ACTION,
+                    PORTRAIT,
+                    LANDSCAPE,
+                    NIGHT,
+                    NIGHT_PORTRAIT,
+                    THEATRE,
+                    BEACH,
+                    SNOW,
+                    SUNSET,
+                    STEADYPHOTO,
+                    FIREWORKS,
+                    SPORTS,
+                    PARTY,
+                    CANDLELIGHT,
+                    BARCODE;
+                }
+
+                public static final Enum UNSUPPORTED = Enum.UNSUPPORTED;
+                public static final Enum FACE_PRIORITY = Enum.FACE_PRIORITY;
+                public static final Enum ACTION = Enum.ACTION;
+                public static final Enum PORTRAIT = Enum.PORTRAIT;
+                public static final Enum LANDSCAPE = Enum.LANDSCAPE;
+                public static final Enum NIGHT = Enum.NIGHT;
+                public static final Enum NIGHT_PORTRAIT = Enum.NIGHT_PORTRAIT;
+                public static final Enum THEATRE = Enum.THEATRE;
+                public static final Enum BEACH = Enum.BEACH;
+                public static final Enum SNOW = Enum.SNOW;
+                public static final Enum SUNSET = Enum.SUNSET;
+                public static final Enum STEADYPHOTO = Enum.STEADYPHOTO;
+                public static final Enum FIREWORKS = Enum.FIREWORKS;
+                public static final Enum SPORTS = Enum.SPORTS;
+                public static final Enum PARTY = Enum.PARTY;
+                public static final Enum CANDLELIGHT = Enum.CANDLELIGHT;
+                public static final Enum BARCODE = Enum.BARCODE;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private SceneModeKey(String name) {
+                    super(name, Control.SceneModeKey.Enum.class);
+                }
+
+                static {
+                    CameraMetadata.registerEnumValues(Control.SceneModeKey.Enum.class, new int[] {
+                        0,  // UNSUPPORTED
+                        1,  // FACE_PRIORITY
+                        2,  // ACTION
+                        3,  // PORTRAIT
+                        4,  // LANDSCAPE
+                        5,  // NIGHT
+                        6,  // NIGHT_PORTRAIT
+                        7,  // THEATRE
+                        8,  // BEACH
+                        9,  // SNOW
+                        10,  // SUNSET
+                        11,  // STEADYPHOTO
+                        12,  // FIREWORKS
+                        13,  // SPORTS
+                        14,  // PARTY
+                        15,  // CANDLELIGHT
+                        16  // BARCODE
+                    });
+                }
+            }
+
+        public static final Key<Control.SceneModeKey.Enum> SCENE_MODE =
+                new SceneModeKey("android.control.sceneMode");
+        public static final Key<Boolean> VIDEO_STABILIZATION_MODE =
+                new Key<Boolean>("android.control.videoStabilizationMode", boolean.class);
+
+    }
+
+    public static final class Edge {
+
+            public static final class ModeKey extends Key<Edge.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, Edge.ModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Edge.ModeKey.Enum> MODE =
+                new ModeKey("android.edge.mode");
+
+    }
+
+    public static final class Flash {
+
+            public static final class ModeKey extends Key<Flash.ModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    SINGLE,
+                    TORCH;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum SINGLE = Enum.SINGLE;
+                public static final Enum TORCH = Enum.TORCH;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private ModeKey(String name) {
+                    super(name, Flash.ModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Flash.ModeKey.Enum> MODE =
+                new ModeKey("android.flash.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<String> GPS_PROCESSING_METHOD =
+                new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
+        public static final Key<Long> GPS_TIMESTAMP =
+                new Key<Long>("android.jpeg.gpsTimestamp", long.class);
+        public static final Key<Integer> ORIENTATION =
+                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<Byte> THUMBNAIL_QUALITY =
+                new Key<Byte>("android.jpeg.thumbnailQuality", byte.class);
+        public static final Key<android.hardware.photography.Size> THUMBNAIL_SIZE =
+                new Key<android.hardware.photography.Size>("android.jpeg.thumbnailSize", android.hardware.photography.Size.class);
+
+    }
+
+    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 =
+                new Key<Float>("android.lens.filterDensity", float.class);
+        public static final Key<Float> FOCAL_LENGTH =
+                new Key<Float>("android.lens.focalLength", float.class);
+        public static final Key<Float> FOCUS_DISTANCE =
+                new Key<Float>("android.lens.focusDistance", float.class);
+
+            public static final class OpticalStabilizationModeKey extends Key<Lens.OpticalStabilizationModeKey.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 OpticalStabilizationModeKey(String name) {
+                    super(name, Lens.OpticalStabilizationModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Lens.OpticalStabilizationModeKey.Enum> OPTICAL_STABILIZATION_MODE =
+                new OpticalStabilizationModeKey("android.lens.opticalStabilizationMode");
+
+    }
+
+    public static final class NoiseReduction {
+
+            public static final class ModeKey extends Key<NoiseReduction.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, NoiseReduction.ModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<NoiseReduction.ModeKey.Enum> MODE =
+                new ModeKey("android.noiseReduction.mode");
+
+    }
+
+    /**
+     * @hide
+     */
+    public static final class Request {
+        /**
+         * @hide
+         */
+        public static final Key<Integer> ID =
+                new Key<Integer>("android.request.id", int.class);
+
+    }
+
+    public static final class Scaler {
+        public static final Key<android.graphics.Rect> CROP_REGION =
+                new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class);
+
+    }
+
+    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 =
+                new Key<Long>("android.sensor.frameDuration", long.class);
+        public static final Key<Integer> SENSITIVITY =
+                new Key<Integer>("android.sensor.sensitivity", int.class);
+
+    }
+
+    public static final class Statistics {
+
+            public static final class FaceDetectModeKey extends Key<Statistics.FaceDetectModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    SIMPLE,
+                    FULL;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum SIMPLE = Enum.SIMPLE;
+                public static final Enum FULL = Enum.FULL;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private FaceDetectModeKey(String name) {
+                    super(name, Statistics.FaceDetectModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Statistics.FaceDetectModeKey.Enum> FACE_DETECT_MODE =
+                new FaceDetectModeKey("android.statistics.faceDetectMode");
+
+    }
+
+    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 =
+                new Key<Float>("android.tonemap.curveGreen", float.class);
+        public static final Key<float[]> CURVE_RED =
+                new Key<float[]>("android.tonemap.curveRed", float[].class);
+
+            public static final class ModeKey extends Key<Tonemap.ModeKey.Enum> {
+                public enum Enum {
+                    CONTRAST_CURVE,
+                    FAST,
+                    HIGH_QUALITY;
+                }
+
+                public static final Enum CONTRAST_CURVE = Enum.CONTRAST_CURVE;
+                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, Tonemap.ModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Tonemap.ModeKey.Enum> MODE =
+                new ModeKey("android.tonemap.mode");
+
+    }
+
+    /**
+     * @hide
+     */
+    public static final class Led {
+        /**
+         * @hide
+         */
+        public static final Key<Boolean> TRANSMIT =
+                new Key<Boolean>("android.led.transmit", boolean.class);
+
+    }
+
+    public static final class BlackLevel {
+        public static final Key<Boolean> LOCK =
+                new Key<Boolean>("android.blackLevel.lock", boolean.class);
+
+    }
+
+}
+
+
diff --git a/core/java/android/hardware/photography/CaptureResult.java b/core/java/android/hardware/photography/CaptureResult.java
index b502c4c..2fdd466 100644
--- a/core/java/android/hardware/photography/CaptureResult.java
+++ b/core/java/android/hardware/photography/CaptureResult.java
@@ -42,15 +42,15 @@
      * or {@link android.media.Image#getTimestamp Image.getTimestamp()} for this
      * capture's image data.
      */
-    public static final Key SENSOR_TIMESTAMP =
-            new Key<Long>("android.sensor.timestamp");
+    public static final Key<Long> SENSOR_TIMESTAMP =
+            new Key<Long>("android.sensor.timestamp", Long.TYPE);
 
     /**
      * The state of the camera device's auto-exposure algorithm. One of the
      * CONTROL_AE_STATE_* enumerations.
      */
-    public static final Key CONTROL_AE_STATE =
-            new Key<Integer>("android.control.aeState");
+    public static final Key<Integer> CONTROL_AE_STATE =
+            new Key<Integer>("android.control.aeState", Integer.TYPE);
 
     /**
      * The auto-exposure algorithm is inactive.
@@ -96,8 +96,8 @@
      * The list of faces detected in this capture. Available if face detection
      * was enabled for this capture
      */
-    public static final Key STATISTICS_DETECTED_FACES =
-            new Key<Face[]>("android.statistics.faces");
+    public static final Key<Face[]> STATISTICS_DETECTED_FACES =
+            new Key<Face[]>("android.statistics.faces", Face[].class);
 
     // TODO: Many many more
 
diff --git a/core/java/android/hardware/photography/CaptureResultKeys.java b/core/java/android/hardware/photography/CaptureResultKeys.java
new file mode 100644
index 0000000..4931564
--- /dev/null
+++ b/core/java/android/hardware/photography/CaptureResultKeys.java
@@ -0,0 +1,533 @@
+/*
+ * 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.hardware.photography;
+
+import static android.hardware.photography.CameraMetadata.Key;
+
+/**
+ * ! Do not edit this file directly !
+ *
+ * Generated automatically from CaptureResultKeys.mako
+ *
+ * TODO: Include a hash of the input files here that the build can check.
+ */
+
+/**
+ * The base class for camera controls and information.
+ *
+ * This class defines the basic key/value map used for querying for camera
+ * characteristics or capture results, and for setting camera request
+ * parameters.
+ *
+ * @see CaptureResult
+ * @see CameraMetadata
+ * @hide
+ **/
+public final class CaptureResultKeys {
+    public static final class ColorCorrection {
+        public static final Key<Rational[]> TRANSFORM =
+                new Key<Rational[]>("android.colorCorrection.transform", Rational[].class);
+        public static final Key<float[]> GAINS =
+                new Key<float[]>("android.colorCorrection.gains", float[].class);
+
+    }
+
+    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 =
+                new Key<int[]>("android.control.aeRegions", int[].class);
+
+            public static final class AeStateKey extends Key<Control.AeStateKey.Enum> {
+                public enum Enum {
+                    INACTIVE,
+                    SEARCHING,
+                    CONVERGED,
+                    LOCKED,
+                    FLASH_REQUIRED,
+                    PRECAPTURE;
+                }
+
+                public static final Enum INACTIVE = Enum.INACTIVE;
+                public static final Enum SEARCHING = Enum.SEARCHING;
+                public static final Enum CONVERGED = Enum.CONVERGED;
+                public static final Enum LOCKED = Enum.LOCKED;
+                public static final Enum FLASH_REQUIRED = Enum.FLASH_REQUIRED;
+                public static final Enum PRECAPTURE = Enum.PRECAPTURE;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AeStateKey(String name) {
+                    super(name, Control.AeStateKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.AeStateKey.Enum> AE_STATE =
+                new AeStateKey("android.control.aeState");
+
+            public static final class AfModeKey extends Key<Control.AfModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    AUTO,
+                    MACRO,
+                    CONTINUOUS_VIDEO,
+                    CONTINUOUS_PICTURE,
+                    EDOF;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum AUTO = Enum.AUTO;
+                public static final Enum MACRO = Enum.MACRO;
+                public static final Enum CONTINUOUS_VIDEO = Enum.CONTINUOUS_VIDEO;
+                public static final Enum CONTINUOUS_PICTURE = Enum.CONTINUOUS_PICTURE;
+                public static final Enum EDOF = Enum.EDOF;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AfModeKey(String name) {
+                    super(name, Control.AfModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.AfModeKey.Enum> AF_MODE =
+                new AfModeKey("android.control.afMode");
+        public static final Key<int[]> AF_REGIONS =
+                new Key<int[]>("android.control.afRegions", int[].class);
+
+            public static final class AfStateKey extends Key<Control.AfStateKey.Enum> {
+                public enum Enum {
+                    INACTIVE,
+                    PASSIVE_SCAN,
+                    PASSIVE_FOCUSED,
+                    ACTIVE_SCAN,
+                    FOCUSED_LOCKED,
+                    NOT_FOCUSED_LOCKED;
+                }
+
+                public static final Enum INACTIVE = Enum.INACTIVE;
+                public static final Enum PASSIVE_SCAN = Enum.PASSIVE_SCAN;
+                public static final Enum PASSIVE_FOCUSED = Enum.PASSIVE_FOCUSED;
+                public static final Enum ACTIVE_SCAN = Enum.ACTIVE_SCAN;
+                public static final Enum FOCUSED_LOCKED = Enum.FOCUSED_LOCKED;
+                public static final Enum NOT_FOCUSED_LOCKED = Enum.NOT_FOCUSED_LOCKED;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AfStateKey(String name) {
+                    super(name, Control.AfStateKey.Enum.class);
+                }
+
+            }
+
+        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);
+
+            public static final class AwbModeKey extends Key<Control.AwbModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    AUTO,
+                    INCANDESCENT,
+                    FLUORESCENT,
+                    WARM_FLUORESCENT,
+                    DAYLIGHT,
+                    CLOUDY_DAYLIGHT,
+                    TWILIGHT,
+                    SHADE;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum AUTO = Enum.AUTO;
+                public static final Enum INCANDESCENT = Enum.INCANDESCENT;
+                public static final Enum FLUORESCENT = Enum.FLUORESCENT;
+                public static final Enum WARM_FLUORESCENT = Enum.WARM_FLUORESCENT;
+                public static final Enum DAYLIGHT = Enum.DAYLIGHT;
+                public static final Enum CLOUDY_DAYLIGHT = Enum.CLOUDY_DAYLIGHT;
+                public static final Enum TWILIGHT = Enum.TWILIGHT;
+                public static final Enum SHADE = Enum.SHADE;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AwbModeKey(String name) {
+                    super(name, Control.AwbModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.AwbModeKey.Enum> AWB_MODE =
+                new AwbModeKey("android.control.awbMode");
+        public static final Key<int[]> AWB_REGIONS =
+                new Key<int[]>("android.control.awbRegions", int[].class);
+
+            public static final class AwbStateKey extends Key<Control.AwbStateKey.Enum> {
+                public enum Enum {
+                    INACTIVE,
+                    SEARCHING,
+                    CONVERGED,
+                    LOCKED;
+                }
+
+                public static final Enum INACTIVE = Enum.INACTIVE;
+                public static final Enum SEARCHING = Enum.SEARCHING;
+                public static final Enum CONVERGED = Enum.CONVERGED;
+                public static final Enum LOCKED = Enum.LOCKED;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private AwbStateKey(String name) {
+                    super(name, Control.AwbStateKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.AwbStateKey.Enum> AWB_STATE =
+                new AwbStateKey("android.control.awbState");
+
+            public static final class ModeKey extends Key<Control.ModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    AUTO,
+                    USE_SCENE_MODE;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum AUTO = Enum.AUTO;
+                public static final Enum USE_SCENE_MODE = Enum.USE_SCENE_MODE;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private ModeKey(String name) {
+                    super(name, Control.ModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Control.ModeKey.Enum> MODE =
+                new ModeKey("android.control.mode");
+
+    }
+
+    public static final class Edge {
+
+            public static final class ModeKey extends Key<Edge.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, Edge.ModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Edge.ModeKey.Enum> MODE =
+                new ModeKey("android.edge.mode");
+
+    }
+
+    public static final class Flash {
+
+            public static final class ModeKey extends Key<Flash.ModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    SINGLE,
+                    TORCH;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum SINGLE = Enum.SINGLE;
+                public static final Enum TORCH = Enum.TORCH;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private ModeKey(String name) {
+                    super(name, Flash.ModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Flash.ModeKey.Enum> MODE =
+                new ModeKey("android.flash.mode");
+
+            public static final class StateKey extends Key<Flash.StateKey.Enum> {
+                public enum Enum {
+                    UNAVAILABLE,
+                    CHARGING,
+                    READY,
+                    FIRED;
+                }
+
+                public static final Enum UNAVAILABLE = Enum.UNAVAILABLE;
+                public static final Enum CHARGING = Enum.CHARGING;
+                public static final Enum READY = Enum.READY;
+                public static final Enum FIRED = Enum.FIRED;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private StateKey(String name) {
+                    super(name, Flash.StateKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Flash.StateKey.Enum> STATE =
+                new StateKey("android.flash.state");
+
+    }
+
+    public static final class Jpeg {
+        public static final Key<double[]> GPS_COORDINATES =
+                new Key<double[]>("android.jpeg.gpsCoordinates", double[].class);
+        public static final Key<String> GPS_PROCESSING_METHOD =
+                new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
+        public static final Key<Long> GPS_TIMESTAMP =
+                new Key<Long>("android.jpeg.gpsTimestamp", long.class);
+        public static final Key<Integer> ORIENTATION =
+                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<Byte> THUMBNAIL_QUALITY =
+                new Key<Byte>("android.jpeg.thumbnailQuality", byte.class);
+        public static final Key<android.hardware.photography.Size> THUMBNAIL_SIZE =
+                new Key<android.hardware.photography.Size>("android.jpeg.thumbnailSize", android.hardware.photography.Size.class);
+
+    }
+
+    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 =
+                new Key<Float>("android.lens.filterDensity", float.class);
+        public static final Key<Float> FOCAL_LENGTH =
+                new Key<Float>("android.lens.focalLength", float.class);
+        public static final Key<Float> FOCUS_DISTANCE =
+                new Key<Float>("android.lens.focusDistance", float.class);
+        public static final Key<Float> FOCUS_RANGE =
+                new Key<Float>("android.lens.focusRange", float.class);
+
+            public static final class OpticalStabilizationModeKey extends Key<Lens.OpticalStabilizationModeKey.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 OpticalStabilizationModeKey(String name) {
+                    super(name, Lens.OpticalStabilizationModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Lens.OpticalStabilizationModeKey.Enum> OPTICAL_STABILIZATION_MODE =
+                new OpticalStabilizationModeKey("android.lens.opticalStabilizationMode");
+
+            public static final class StateKey extends Key<Lens.StateKey.Enum> {
+                public enum Enum {
+                    STATIONARY;
+                }
+
+                public static final Enum STATIONARY = Enum.STATIONARY;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private StateKey(String name) {
+                    super(name, Lens.StateKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Lens.StateKey.Enum> STATE =
+                new StateKey("android.lens.state");
+
+    }
+
+    public static final class NoiseReduction {
+
+            public static final class ModeKey extends Key<NoiseReduction.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, NoiseReduction.ModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<NoiseReduction.ModeKey.Enum> MODE =
+                new ModeKey("android.noiseReduction.mode");
+
+    }
+
+    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 Scaler {
+        public static final Key<android.graphics.Rect> CROP_REGION =
+                new Key<android.graphics.Rect>("android.scaler.cropRegion", android.graphics.Rect.class);
+
+    }
+
+    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 =
+                new Key<Long>("android.sensor.frameDuration", long.class);
+        public static final Key<Integer> SENSITIVITY =
+                new Key<Integer>("android.sensor.sensitivity", int.class);
+        public static final Key<Long> TIMESTAMP =
+                new Key<Long>("android.sensor.timestamp", long.class);
+
+    }
+
+    public static final class Statistics {
+
+            public static final class FaceDetectModeKey extends Key<Statistics.FaceDetectModeKey.Enum> {
+                public enum Enum {
+                    OFF,
+                    SIMPLE,
+                    FULL;
+                }
+
+                public static final Enum OFF = Enum.OFF;
+                public static final Enum SIMPLE = Enum.SIMPLE;
+                public static final Enum FULL = Enum.FULL;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private FaceDetectModeKey(String name) {
+                    super(name, Statistics.FaceDetectModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Statistics.FaceDetectModeKey.Enum> FACE_DETECT_MODE =
+                new FaceDetectModeKey("android.statistics.faceDetectMode");
+        public static final Key<int[]> FACE_IDS =
+                new Key<int[]>("android.statistics.faceIds", int[].class);
+        public static final Key<int[]> FACE_LANDMARKS =
+                new Key<int[]>("android.statistics.faceLandmarks", int[].class);
+        public static final Key<android.graphics.Rect[]> FACE_RECTANGLES =
+                new Key<android.graphics.Rect[]>("android.statistics.faceRectangles", android.graphics.Rect[].class);
+        public static final Key<byte[]> FACE_SCORES =
+                new Key<byte[]>("android.statistics.faceScores", byte[].class);
+        public static final Key<float[]> LENS_SHADING_MAP =
+                new Key<float[]>("android.statistics.lensShadingMap", float[].class);
+        public static final Key<float[]> PREDICTED_COLOR_GAINS =
+                new Key<float[]>("android.statistics.predictedColorGains", float[].class);
+        public static final Key<Rational[]> PREDICTED_COLOR_TRANSFORM =
+                new Key<Rational[]>("android.statistics.predictedColorTransform", Rational[].class);
+
+            public static final class SceneFlickerKey extends Key<Statistics.SceneFlickerKey.Enum> {
+                public enum Enum {
+                    NONE,
+                    _50HZ,
+                    _60HZ;
+                }
+
+                public static final Enum NONE = Enum.NONE;
+                public static final Enum _50HZ = Enum._50HZ;
+                public static final Enum _60HZ = Enum._60HZ;
+
+                // TODO: remove requirement for constructor by making Key an interface
+                private SceneFlickerKey(String name) {
+                    super(name, Statistics.SceneFlickerKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Statistics.SceneFlickerKey.Enum> SCENE_FLICKER =
+                new SceneFlickerKey("android.statistics.sceneFlicker");
+
+    }
+
+    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 =
+                new Key<Float>("android.tonemap.curveGreen", float.class);
+        public static final Key<float[]> CURVE_RED =
+                new Key<float[]>("android.tonemap.curveRed", float[].class);
+
+            public static final class ModeKey extends Key<Tonemap.ModeKey.Enum> {
+                public enum Enum {
+                    CONTRAST_CURVE,
+                    FAST,
+                    HIGH_QUALITY;
+                }
+
+                public static final Enum CONTRAST_CURVE = Enum.CONTRAST_CURVE;
+                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, Tonemap.ModeKey.Enum.class);
+                }
+
+            }
+
+        public static final Key<Tonemap.ModeKey.Enum> MODE =
+                new ModeKey("android.tonemap.mode");
+
+    }
+
+    /**
+     * @hide
+     */
+    public static final class Led {
+        /**
+         * @hide
+         */
+        public static final Key<Boolean> TRANSMIT =
+                new Key<Boolean>("android.led.transmit", boolean.class);
+
+    }
+
+    public static final class BlackLevel {
+        public static final Key<Boolean> LOCK =
+                new Key<Boolean>("android.blackLevel.lock", boolean.class);
+
+    }
+
+}
+
+
diff --git a/core/java/android/hardware/photography/ICameraDeviceUser.aidl b/core/java/android/hardware/photography/ICameraDeviceUser.aidl
index d1310fb..1e58bab 100644
--- a/core/java/android/hardware/photography/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/photography/ICameraDeviceUser.aidl
@@ -41,4 +41,6 @@
     int createStream(int width, int height, int format, in Surface surface);
 
     int createDefaultRequest(int templateId, out CameraMetadata request);
+
+    int getCameraInfo(out CameraMetadata info);
 }
diff --git a/core/java/android/hardware/photography/Rational.java b/core/java/android/hardware/photography/Rational.java
new file mode 100644
index 0000000..66e533e
--- /dev/null
+++ b/core/java/android/hardware/photography/Rational.java
@@ -0,0 +1,162 @@
+/*
+ * 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.hardware.photography;
+
+/**
+ * The rational data type used by CameraMetadata keys. Contains a pair of ints representing the
+ * numerator and denominator of a Rational number. This type is immutable.
+ */
+public final class Rational {
+    private final int mNumerator;
+    private final int mDenominator;
+
+    /**
+     * <p>Create a Rational with a given numerator and denominator.</p>
+     *
+     * <p>
+     * The signs of the numerator and the denominator may be flipped such that the denominator
+     * is always 0.
+     * </p>
+     *
+     * @param numerator the numerator of the rational
+     * @param denominator the denominator of the rational
+     *
+     * @throws IllegalArgumentException if the denominator is 0
+     */
+    public Rational(int numerator, int denominator) {
+
+        if (denominator == 0) {
+            throw new IllegalArgumentException("Argument 'denominator' is 0");
+        }
+
+        if (denominator < 0) {
+            numerator = -numerator;
+            denominator = -denominator;
+        }
+
+        mNumerator = numerator;
+        mDenominator = denominator;
+    }
+
+    /**
+     * Gets the numerator of the rational.
+     */
+    public int getNumerator() {
+        return mNumerator;
+    }
+
+    /**
+     * Gets the denominator of the rational
+     */
+    public int getDenominator() {
+        return mDenominator;
+    }
+
+    /**
+     * <p>Compare this Rational to another object and see if they are equal.</p>
+     *
+     * <p>A Rational object can only be equal to another Rational object (comparing against any other
+     * type will return false).</p>
+     *
+     * <p>A Rational object is considered equal to another Rational object if and only if their
+     * reduced forms have the same numerator and denominator.</p>
+     *
+     * <p>A reduced form of a Rational is calculated by dividing both the numerator and the
+     * denominator by their greatest common divisor.</p>
+     *
+     * <pre>
+     *      (new Rational(1, 2)).equals(new Rational(1, 2)) == true  // trivially true
+     *      (new Rational(2, 3)).equals(new Rational(1, 2)) == false // trivially false
+     *      (new Rational(1, 2)).equals(new Rational(2, 4)) == true  // true after reduction
+     * </pre>
+     *
+     * @param obj a reference to another object
+     *
+     * @return boolean that determines whether or not the two Rational objects are equal.
+     */
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof Rational) {
+            Rational other = (Rational) obj;
+            if(mNumerator == other.mNumerator && mDenominator == other.mDenominator) {
+                return true;
+            } else {
+                int thisGcd = gcd();
+                int otherGcd = other.gcd();
+
+                int thisNumerator = mNumerator / thisGcd;
+                int thisDenominator = mDenominator / thisGcd;
+
+                int otherNumerator = other.mNumerator / otherGcd;
+                int otherDenominator = other.mDenominator / otherGcd;
+
+                return (thisNumerator == otherNumerator && thisDenominator == otherDenominator);
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return mNumerator + "/" + mDenominator;
+    }
+
+    @Override
+    public int hashCode() {
+        final long INT_MASK = 0xffffffffL;
+
+        long asLong = INT_MASK & mNumerator;
+        asLong <<= 32;
+
+        asLong |= (INT_MASK & mDenominator);
+
+        return ((Long)asLong).hashCode();
+    }
+
+    /**
+     * Calculates the greatest common divisor using Euclid's algorithm.
+     *
+     * @return int value representing the gcd. Always positive.
+     * @hide
+     */
+    public int gcd() {
+        /**
+         * Non-recursive implementation of Euclid's algorithm:
+         *
+         *  gcd(a, 0) := a
+         *  gcd(a, b) := gcd(b, a mod b)
+         *
+         */
+
+        int a = mNumerator;
+        int b = mDenominator;
+
+        while (b != 0) {
+            int oldB = b;
+
+            b = a % b;
+            a = oldB;
+        }
+
+        return Math.abs(a);
+    }
+}
diff --git a/core/java/android/hardware/photography/Size.java b/core/java/android/hardware/photography/Size.java
index e1115d3..499fd56 100644
--- a/core/java/android/hardware/photography/Size.java
+++ b/core/java/android/hardware/photography/Size.java
@@ -24,12 +24,12 @@
     /**
      * Create a new immutable Size instance
      *
-     * @param w The width to store in the Size instance
-     * @param h The height to store in the Size instance
+     * @param width The width to store in the Size instance
+     * @param height The height to store in the Size instance
      */
-    Size(int w, int h) {
-        mWidth = w;
-        mHeight = h;
+    public Size(int width, int height) {
+        mWidth = width;
+        mHeight = height;
     }
 
     public final int getWidth() {
@@ -40,6 +40,38 @@
         return mHeight;
     }
 
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (this == obj) {
+            return true;
+        }
+        if (obj instanceof Size) {
+            Size other = (Size) obj;
+            return mWidth == other.mWidth && mHeight == other.mHeight;
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return mWidth + "x" + mHeight;
+    }
+
+    @Override
+    public int hashCode() {
+        final long INT_MASK = 0xffffffffL;
+
+        long asLong = INT_MASK & mWidth;
+        asLong <<= 32;
+
+        asLong |= (INT_MASK & mHeight);
+
+        return ((Long)asLong).hashCode();
+    }
+
     private final int mWidth;
     private final int mHeight;
 };
diff --git a/core/java/android/hardware/photography/impl/CameraDevice.java b/core/java/android/hardware/photography/impl/CameraDevice.java
index b1e3f6a..802cb833 100644
--- a/core/java/android/hardware/photography/impl/CameraDevice.java
+++ b/core/java/android/hardware/photography/impl/CameraDevice.java
@@ -48,6 +48,7 @@
     private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
 
     // XX: Make this a WeakReference<CaptureListener> ?
+    // TODO: Convert to SparseIntArray
     private final HashMap<Integer, CaptureListenerHolder> mCaptureListenerMap =
             new HashMap<Integer, CaptureListenerHolder>();
 
@@ -73,9 +74,21 @@
 
     @Override
     public CameraProperties getProperties() throws CameraAccessException {
-        // TODO
-        Log.v(TAG, "TODO: Implement getProperties");
-        return new CameraProperties();
+
+        CameraProperties properties = new CameraProperties();
+        CameraMetadata info = new CameraMetadata();
+
+        try {
+            mRemoteDevice.getCameraInfo(/*out*/info);
+        } catch(CameraRuntimeException e) {
+            throw e.asChecked();
+        } catch(RemoteException e) {
+            // impossible
+            return null;
+        }
+
+        properties.swap(info);
+        return properties;
     }
 
     @Override
@@ -114,9 +127,6 @@
             }
 
             CaptureRequest request = new CaptureRequest();
-
-            // XX: could also change binder signature but that's more work than
-            // just using swap.
             request.swap(templatedRequest);
 
             return request;
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalClass.java b/core/java/android/hardware/photography/impl/MetadataMarshalClass.java
new file mode 100644
index 0000000..a70784d
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalClass.java
@@ -0,0 +1,67 @@
+/*
+ * 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.hardware.photography.impl;
+
+import java.nio.ByteBuffer;
+
+public interface MetadataMarshalClass<T> {
+
+    /**
+     * Marshal the specified object instance (value) into a byte buffer.
+     *
+     * @param value the value of type T that we wish to write into the byte buffer
+     * @param buffer the byte buffer into which the marshalled object will be written
+     * @param nativeType the native type, e.g.
+     *        {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}.
+     *        Guaranteed to be one for which isNativeTypeSupported returns true.
+     * @param sizeOnly if this is true, don't write to the byte buffer. calculate the size only.
+     * @return the size that needs to be written to the byte buffer
+     */
+    int marshal(T value, ByteBuffer buffer, int nativeType, boolean sizeOnly);
+
+    /**
+     * Unmarshal a new object instance from the byte buffer.
+     * @param buffer the byte buffer, from which we will read the object
+     * @param nativeType the native type, e.g.
+     *        {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}.
+     *        Guaranteed to be one for which isNativeTypeSupported returns true.
+     * @return a new instance of type T read from the byte buffer
+     */
+    T unmarshal(ByteBuffer buffer, int nativeType);
+
+    Class<T> getMarshalingClass();
+
+    /**
+     * Determines whether or not this marshaller supports this native type. Most marshallers
+     * will are likely to only support one type.
+     *
+     * @param nativeType the native type, e.g.
+     *        {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}
+     * @return true if it supports, false otherwise
+     */
+    boolean isNativeTypeSupported(int nativeType);
+
+    public static int NATIVE_SIZE_DYNAMIC = -1;
+
+    /**
+     * How many bytes T will take up if marshalled to/from nativeType
+     * @param nativeType the native type, e.g.
+     *        {@link android.hardware.photography.CameraMetadata#TYPE_BYTE TYPE_BYTE}
+     * @return a size in bytes, or NATIVE_SIZE_DYNAMIC if the size is dynamic
+     */
+    int getNativeSize(int nativeType);
+}
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalRect.java b/core/java/android/hardware/photography/impl/MetadataMarshalRect.java
new file mode 100644
index 0000000..d6636ac
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalRect.java
@@ -0,0 +1,68 @@
+/*
+ * 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.hardware.photography.impl;
+
+import android.graphics.Rect;
+import android.hardware.photography.CameraMetadata;
+
+import java.nio.ByteBuffer;
+
+public class MetadataMarshalRect implements MetadataMarshalClass<Rect> {
+    private static final int SIZE = 16;
+
+    @Override
+    public int marshal(Rect value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
+        if (sizeOnly) {
+            return SIZE;
+        }
+
+        buffer.putInt(value.left);
+        buffer.putInt(value.top);
+        buffer.putInt(value.width());
+        buffer.putInt(value.height());
+
+        return SIZE;
+    }
+
+    @Override
+    public Rect unmarshal(ByteBuffer buffer, int nativeType) {
+
+        int left = buffer.getInt();
+        int top = buffer.getInt();
+        int width = buffer.getInt();
+        int height = buffer.getInt();
+
+        int right = left + width;
+        int bottom = top + height;
+
+        return new Rect(left, top, right, bottom);
+    }
+
+    @Override
+    public Class<Rect> getMarshalingClass() {
+        return Rect.class;
+    }
+
+    @Override
+    public boolean isNativeTypeSupported(int nativeType) {
+        return nativeType == CameraMetadata.TYPE_INT32;
+    }
+
+    @Override
+    public int getNativeSize(int nativeType) {
+        return SIZE;
+    }
+}
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalSize.java b/core/java/android/hardware/photography/impl/MetadataMarshalSize.java
new file mode 100644
index 0000000..430219c
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalSize.java
@@ -0,0 +1,61 @@
+/*
+ * 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.hardware.photography.impl;
+
+import android.hardware.photography.CameraMetadata;
+import android.hardware.photography.Size;
+
+import java.nio.ByteBuffer;
+
+public class MetadataMarshalSize implements MetadataMarshalClass<Size> {
+
+    private static final int SIZE = 8;
+
+    @Override
+    public int marshal(Size value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
+        if (sizeOnly) {
+            return SIZE;
+        }
+
+        buffer.putInt(value.getWidth());
+        buffer.putInt(value.getHeight());
+
+        return SIZE;
+    }
+
+    @Override
+    public Size unmarshal(ByteBuffer buffer, int nativeType) {
+        int width = buffer.getInt();
+        int height = buffer.getInt();
+
+        return new Size(width, height);
+    }
+
+    @Override
+    public Class<Size> getMarshalingClass() {
+        return Size.class;
+    }
+
+    @Override
+    public boolean isNativeTypeSupported(int nativeType) {
+        return nativeType == CameraMetadata.TYPE_INT32;
+    }
+
+    @Override
+    public int getNativeSize(int nativeType) {
+        return SIZE;
+    }
+}
diff --git a/core/java/android/hardware/photography/impl/MetadataMarshalString.java b/core/java/android/hardware/photography/impl/MetadataMarshalString.java
new file mode 100644
index 0000000..81123ee
--- /dev/null
+++ b/core/java/android/hardware/photography/impl/MetadataMarshalString.java
@@ -0,0 +1,81 @@
+/*
+ * 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.hardware.photography.impl;
+
+import android.hardware.photography.CameraMetadata;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+
+public class MetadataMarshalString implements MetadataMarshalClass<String> {
+
+    private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
+
+    @Override
+    public int marshal(String value, ByteBuffer buffer, int nativeType, boolean sizeOnly) {
+        byte[] arr = value.getBytes(UTF8_CHARSET);
+
+        if (!sizeOnly) {
+            buffer.put(arr);
+            buffer.put((byte)0); // metadata strings are NULL-terminated
+        }
+
+        return arr.length + 1;
+    }
+
+    @Override
+    public String unmarshal(ByteBuffer buffer, int nativeType) {
+
+        buffer.mark(); // save the current position
+
+        boolean foundNull = false;
+        int stringLength = 0;
+        while (buffer.hasRemaining()) {
+            if (buffer.get() == (byte)0) {
+                foundNull = true;
+                break;
+            }
+
+            stringLength++;
+        }
+        if (!foundNull) {
+            throw new IllegalArgumentException("Strings must be null-terminated");
+        }
+
+        buffer.reset(); // go back to the previously marked position
+
+        byte[] strBytes = new byte[stringLength + 1];
+        buffer.get(strBytes, /*dstOffset*/0, stringLength + 1); // including null character
+
+        // not including null character
+        return new String(strBytes, /*offset*/0, stringLength, UTF8_CHARSET);
+    }
+
+    @Override
+    public Class<String> getMarshalingClass() {
+        return String.class;
+    }
+
+    @Override
+    public boolean isNativeTypeSupported(int nativeType) {
+        return nativeType == CameraMetadata.TYPE_BYTE;
+    }
+
+    @Override
+    public int getNativeSize(int nativeType) {
+        return NATIVE_SIZE_DYNAMIC;
+    }
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 04ab0a5..f920874 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1373,4 +1373,16 @@
         }
         return timeOutMs;
     }
+
+    /**
+     * Get the carrier provisioning url.
+     * {@hide}
+     */
+    public String getMobileProvisioningUrl() {
+        try {
+            return mService.getMobileProvisioningUrl();
+        } catch (RemoteException e) {
+        }
+        return null;
+    }
 }
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index 1ebf393..5151a04 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -374,7 +374,7 @@
                 //Do it a bit earlier than half the lease duration time
                 //to beat the native DHCP client and avoid extra packets
                 //48% for one hour lease time = 29 minutes
-                mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                         SystemClock.elapsedRealtime() +
                         leaseDuration * 480, //in milliseconds
                         mDhcpRenewalIntent);
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 3dbe078..d6a3e37 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -37,6 +37,9 @@
 /** {@hide} */
 interface IConnectivityManager
 {
+    // Keep this in sync with framework/native/services/connectivitymanager/ConnectivityManager.h
+    void markSocketAsUser(in ParcelFileDescriptor socket, int uid);
+
     void setNetworkPreference(int pref);
 
     int getNetworkPreference();
@@ -134,4 +137,6 @@
     int findConnectionTypeForIface(in String iface);
 
     int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs, in ResultReceiver resultReceiver);
+
+    String getMobileProvisioningUrl();
 }
diff --git a/core/java/android/net/LocalServerSocket.java b/core/java/android/net/LocalServerSocket.java
index 2b93fc2..a36203b 100644
--- a/core/java/android/net/LocalServerSocket.java
+++ b/core/java/android/net/LocalServerSocket.java
@@ -46,7 +46,7 @@
     {
         impl = new LocalSocketImpl();
 
-        impl.create(true);
+        impl.create(LocalSocket.SOCKET_STREAM);
 
         localAddress = new LocalSocketAddress(name);
         impl.bind(localAddress);
@@ -93,7 +93,7 @@
 
         impl.accept (acceptedImpl);
 
-        return new LocalSocket(acceptedImpl);
+        return new LocalSocket(acceptedImpl, LocalSocket.SOCKET_UNKNOWN);
     }
 
     /**
diff --git a/core/java/android/net/LocalSocket.java b/core/java/android/net/LocalSocket.java
index 14a8094..31bc20b 100644
--- a/core/java/android/net/LocalSocket.java
+++ b/core/java/android/net/LocalSocket.java
@@ -34,21 +34,42 @@
     private LocalSocketAddress localAddress;
     private boolean isBound;
     private boolean isConnected;
+    private final int sockType;
+
+    /** unknown socket type (used for constructor with existing file descriptor) */
+    /* package */ static final int SOCKET_UNKNOWN = 0;
+    /** Datagram socket type */
+    public static final int SOCKET_DGRAM = 1;
+    /** Stream socket type */
+    public static final int SOCKET_STREAM = 2;
+    /** Sequential packet socket type */
+    public static final int SOCKET_SEQPACKET = 3;
 
     /**
      * Creates a AF_LOCAL/UNIX domain stream socket.
      */
     public LocalSocket() {
-        this(new LocalSocketImpl());
+        this(SOCKET_STREAM);
+    }
+
+    /**
+     * Creates a AF_LOCAL/UNIX domain stream socket with given socket type
+     *
+     * @param sockType either {@link #SOCKET_DGRAM}, {@link #SOCKET_STREAM}
+     * or {@link #SOCKET_SEQPACKET}
+     */
+    public LocalSocket(int sockType) {
+        this(new LocalSocketImpl(), sockType);
         isBound = false;
         isConnected = false;
     }
+
     /**
      * Creates a AF_LOCAL/UNIX domain stream socket with FileDescriptor.
      * @hide
      */
     public LocalSocket(FileDescriptor fd) throws IOException {
-        this(new LocalSocketImpl(fd));
+        this(new LocalSocketImpl(fd), SOCKET_UNKNOWN);
         isBound = true;
         isConnected = true;
     }
@@ -57,8 +78,9 @@
      * for use with AndroidServerSocket
      * @param impl a SocketImpl
      */
-    /*package*/ LocalSocket(LocalSocketImpl impl) {
+    /*package*/ LocalSocket(LocalSocketImpl impl, int sockType) {
         this.impl = impl;
+        this.sockType = sockType;
         this.isConnected = false;
         this.isBound = false;
     }
@@ -81,7 +103,7 @@
             synchronized (this) {
                 if (!implCreated) {
                     try {
-                        impl.create(true);
+                        impl.create(sockType);
                     } finally {
                         implCreated = true;
                     }
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index 3b43c36..fa3cf58 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -22,6 +22,10 @@
 import java.io.FileDescriptor;
 import java.net.SocketOptions;
 
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+import libcore.io.OsConstants;
+
 /**
  * Socket implementation used for android.net.LocalSocket and
  * android.net.LocalServerSocket. Supports only AF_LOCAL sockets.
@@ -159,7 +163,6 @@
 
     private native int pending_native(FileDescriptor fd) throws IOException;
     private native int available_native(FileDescriptor fd) throws IOException;
-    private native void close_native(FileDescriptor fd) throws IOException;
     private native int read_native(FileDescriptor fd) throws IOException;
     private native int readba_native(byte[] b, int off, int len,
             FileDescriptor fd) throws IOException;
@@ -171,8 +174,6 @@
             int namespace) throws IOException;
     private native void bindLocal(FileDescriptor fd, String name, int namespace)
             throws IOException;
-    private native FileDescriptor create_native(boolean stream)
-            throws IOException;
     private native void listen_native(FileDescriptor fd, int backlog)
             throws IOException;
     private native void shutdown(FileDescriptor fd, boolean shutdownInput);
@@ -222,15 +223,33 @@
     /**
      * Creates a socket in the underlying OS.
      *
-     * @param stream true if this should be a stream socket, false for
-     * datagram.
+     * @param sockType either {@link LocalSocket#SOCKET_DGRAM}, {@link LocalSocket#SOCKET_STREAM}
+     * or {@link LocalSocket#SOCKET_SEQPACKET}
      * @throws IOException
      */
-    public void create (boolean stream) throws IOException {
+    public void create (int sockType) throws IOException {
         // no error if socket already created
         // need this for LocalServerSocket.accept()
         if (fd == null) {
-            fd = create_native(stream);
+            int osType;
+            switch (sockType) {
+                case LocalSocket.SOCKET_DGRAM:
+                    osType = OsConstants.SOCK_DGRAM;
+                    break;
+                case LocalSocket.SOCKET_STREAM:
+                    osType = OsConstants.SOCK_STREAM;
+                    break;
+                case LocalSocket.SOCKET_SEQPACKET:
+                    osType = OsConstants.SOCK_SEQPACKET;
+                    break;
+                default:
+                    throw new IllegalStateException("unknown sockType");
+            }
+            try {
+                fd = Libcore.os.socket(OsConstants.AF_UNIX, osType, 0);
+            } catch (ErrnoException e) {
+                e.rethrowAsIOException();
+            }
         }
     }
 
@@ -242,7 +261,11 @@
     public void close() throws IOException {
         synchronized (LocalSocketImpl.this) {
             if (fd == null) return;
-            close_native(fd);
+            try {
+                Libcore.os.close(fd);
+            } catch (ErrnoException e) {
+                e.rethrowAsIOException();
+            }
             fd = null;
         }
     }
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 382b25e..62d8738 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -639,6 +639,7 @@
         @Deprecated
         public static long[] readFullLongArray(DataInputStream in) throws IOException {
             final int size = in.readInt();
+            if (size < 0) throw new ProtocolException("negative array size");
             final long[] values = new long[size];
             for (int i = 0; i < values.length; i++) {
                 values[i] = in.readLong();
@@ -680,6 +681,7 @@
         public static long[] readVarLongArray(DataInputStream in) throws IOException {
             final int size = in.readInt();
             if (size == -1) return null;
+            if (size < 0) throw new ProtocolException("negative array size");
             final long[] values = new long[size];
             for (int i = 0; i < values.length; i++) {
                 values[i] = readVarLong(in);
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 333fcc6..b24d396 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -104,6 +104,11 @@
     public native static String getDhcpError();
 
     /**
+     * Set the SO_MARK of {@code socketfd} to {@code mark}
+     */
+    public native static void markSocket(int socketfd, int mark);
+
+    /**
      * Convert a IPv4 address from an integer to an InetAddress.
      * @param hostAddress an int corresponding to the IPv4 address in network byte order
      */
diff --git a/core/java/android/net/VpnService.java b/core/java/android/net/VpnService.java
index 733de94..d7dc7f5 100644
--- a/core/java/android/net/VpnService.java
+++ b/core/java/android/net/VpnService.java
@@ -36,6 +36,7 @@
 import java.net.InetAddress;
 import java.net.Socket;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * VpnService is a base class for applications to extend and build their
@@ -253,8 +254,8 @@
     public class Builder {
 
         private final VpnConfig mConfig = new VpnConfig();
-        private final StringBuilder mAddresses = new StringBuilder();
-        private final StringBuilder mRoutes = new StringBuilder();
+        private final List<LinkAddress> mAddresses = new ArrayList<LinkAddress>();
+        private final List<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
 
         public Builder() {
             mConfig.user = VpnService.this.getClass().getName();
@@ -328,9 +329,7 @@
             if (address.isAnyLocalAddress()) {
                 throw new IllegalArgumentException("Bad address");
             }
-
-            mAddresses.append(' ')
-                    .append(address.getHostAddress()).append('/').append(prefixLength);
+            mAddresses.add(new LinkAddress(address, prefixLength));
             return this;
         }
 
@@ -364,8 +363,7 @@
                     }
                 }
             }
-
-            mRoutes.append(' ').append(address.getHostAddress()).append('/').append(prefixLength);
+            mRoutes.add(new RouteInfo(new LinkAddress(address, prefixLength), null));
             return this;
         }
 
@@ -466,8 +464,8 @@
          * @see VpnService
          */
         public ParcelFileDescriptor establish() {
-            mConfig.addresses = mAddresses.toString();
-            mConfig.routes = mRoutes.toString();
+            mConfig.addresses = mAddresses;
+            mConfig.routes = mRoutes;
 
             try {
                 return getService().establishVpn(mConfig);
diff --git a/core/java/android/net/http/HttpResponseCache.java b/core/java/android/net/http/HttpResponseCache.java
index bd50bcf..269dfb8 100644
--- a/core/java/android/net/http/HttpResponseCache.java
+++ b/core/java/android/net/http/HttpResponseCache.java
@@ -17,9 +17,9 @@
 package android.net.http;
 
 import android.content.Context;
+import com.android.okhttp.OkResponseCache;
 import com.android.okhttp.ResponseSource;
 import com.android.okhttp.internal.DiskLruCache;
-import com.android.okhttp.internal.http.OkResponseCache;
 import java.io.Closeable;
 import java.io.File;
 import java.io.IOException;
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index b8769b4..f474504 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -16,15 +16,12 @@
 
 package android.os;
 
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
 
 import java.io.Serializable;
 import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
 import java.util.Set;
 
 /**
@@ -37,13 +34,13 @@
 
     static {
         EMPTY = new Bundle();
-        EMPTY.mMap = Collections.unmodifiableMap(new HashMap<String, Object>());
+        EMPTY.mMap = ArrayMap.EMPTY;
     }
 
     // Invariant - exactly one of mMap / mParcelledData will be null
     // (except inside a call to unparcel)
 
-    /* package */ Map<String, Object> mMap = null;
+    /* package */ ArrayMap<String, Object> mMap = null;
 
     /*
      * If mParcelledData is non-null, then mMap will be null and the
@@ -65,7 +62,7 @@
      * Constructs a new, empty Bundle.
      */
     public Bundle() {
-        mMap = new HashMap<String, Object>();
+        mMap = new ArrayMap<String, Object>();
         mClassLoader = getClass().getClassLoader();
     }
 
@@ -91,7 +88,7 @@
      * inside of the Bundle.
      */
     public Bundle(ClassLoader loader) {
-        mMap = new HashMap<String, Object>();
+        mMap = new ArrayMap<String, Object>();
         mClassLoader = loader;
     }
 
@@ -102,7 +99,7 @@
      * @param capacity the initial capacity of the Bundle
      */
     public Bundle(int capacity) {
-        mMap = new HashMap<String, Object>(capacity);
+        mMap = new ArrayMap<String, Object>(capacity);
         mClassLoader = getClass().getClassLoader();
     }
 
@@ -122,7 +119,7 @@
         }
 
         if (b.mMap != null) {
-            mMap = new HashMap<String, Object>(b.mMap);
+            mMap = new ArrayMap<String, Object>(b.mMap);
         } else {
             mMap = null;
         }
@@ -162,7 +159,7 @@
         if (size == 0) {
             return null;
         }
-        Object o = mMap.values().iterator().next();
+        Object o = mMap.valueAt(0);
         try {
             return (String) o;
         } catch (ClassCastException e) {
@@ -218,9 +215,12 @@
             return;
         }
         if (mMap == null) {
-            mMap = new HashMap<String, Object>(N);
+            mMap = new ArrayMap<String, Object>(N);
+        } else {
+            mMap.erase();
+            mMap.ensureCapacity(N);
         }
-        mParcelledData.readMapInternal(mMap, N, mClassLoader);
+        mParcelledData.readArrayMapInternal(mMap, N, mClassLoader);
         mParcelledData.recycle();
         mParcelledData = null;
     }
@@ -331,9 +331,8 @@
                 }
             } else {
                 // It's been unparcelled, so we need to walk the map
-                Iterator<Map.Entry<String, Object>> iter = mMap.entrySet().iterator();
-                while (!fdFound && iter.hasNext()) {
-                    Object obj = iter.next().getValue();
+                for (int i=mMap.size()-1; i>=0; i--) {
+                    Object obj = mMap.valueAt(i);
                     if (obj instanceof Parcelable) {
                         if ((((Parcelable)obj).describeContents()
                                 & Parcelable.CONTENTS_FILE_DESCRIPTOR) != 0) {
@@ -1643,7 +1642,7 @@
                 parcel.writeInt(0x4C444E42); // 'B' 'N' 'D' 'L'
     
                 int oldPos = parcel.dataPosition();
-                parcel.writeMapInternal(mMap);
+                parcel.writeArrayMapInternal(mMap);
                 int newPos = parcel.dataPosition();
     
                 // Backpatch length
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/IBinder.java b/core/java/android/os/IBinder.java
index b7bc45f..a2432d6 100644
--- a/core/java/android/os/IBinder.java
+++ b/core/java/android/os/IBinder.java
@@ -238,7 +238,7 @@
      * <p>You will only receive death notifications for remote binders,
      * as local binders by definition can't die without you dying as well.
      * 
-     * @throws Throws {@link RemoteException} if the target IBinder's
+     * @throws RemoteException if the target IBinder's
      * process has already died.
      * 
      * @see #unlinkToDeath
@@ -251,13 +251,13 @@
      * The recipient will no longer be called if this object
      * dies.
      * 
-     * @return Returns true if the <var>recipient</var> is successfully
+     * @return {@code true} if the <var>recipient</var> is successfully
      * unlinked, assuring you that its
      * {@link DeathRecipient#binderDied DeathRecipient.binderDied()} method
-     * will not be called.  Returns false if the target IBinder has already
+     * will not be called;  {@code false} if the target IBinder has already
      * died, meaning the method has been (or soon will be) called.
      * 
-     * @throws Throws {@link java.util.NoSuchElementException} if the given
+     * @throws java.util.NoSuchElementException if the given
      * <var>recipient</var> has not been registered with the IBinder, and
      * the IBinder is still alive.  Note that if the <var>recipient</var>
      * was never registered, but the IBinder has already died, then this
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 30885856..51c367a 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -368,6 +368,26 @@
     void clearMarkedForwarding(String iface);
 
     /**
+     * Get the SO_MARK associated with routing packets for user {@code uid}
+     */
+    int getMarkForUid(int uid);
+
+    /**
+     * Get the SO_MARK associated with protecting packets from VPN routing rules
+     */
+    int getMarkForProtect();
+
+    /**
+     * Route all traffic in {@code route} to {@code iface} setup for marked forwarding
+     */
+    void setMarkedForwardingRoute(String iface, in RouteInfo route);
+
+    /**
+     * Clear routes set by {@link setMarkedForwardingRoute}
+     */
+    void clearMarkedForwardingRoute(String iface, in RouteInfo route);
+
+    /**
      * Set a process (pid) to use the name servers associated with the specified interface.
      */
     void setDnsInterfaceForPid(String iface, int pid);
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index 7589a5a..bd2d9ac 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -49,4 +49,5 @@
     boolean changeRestrictionsPin(in String newPin);
     int checkRestrictionsPin(in String pin);
     boolean hasRestrictionsPin();
+    void removeRestrictions();
 }
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 0916ea9..12f7915 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -17,6 +17,7 @@
 package android.os;
 
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.util.SparseArray;
 import android.util.SparseBooleanArray;
@@ -572,7 +573,7 @@
      * allows you to avoid mysterious type errors at the point of marshalling.
      */
     public final void writeMap(Map val) {
-        writeMapInternal((Map<String,Object>) val);
+        writeMapInternal((Map<String, Object>) val);
     }
 
     /**
@@ -593,6 +594,23 @@
     }
 
     /**
+     * Flatten an ArrayMap into the parcel at the current dataPosition(),
+     * growing dataCapacity() if needed.  The Map keys must be String objects.
+     */
+    /* package */ void writeArrayMapInternal(ArrayMap<String,Object> val) {
+        if (val == null) {
+            writeInt(-1);
+            return;
+        }
+        final int N = val.size();
+        writeInt(N);
+        for (int i=0; i<N; i++) {
+            writeValue(val.keyAt(i));
+            writeValue(val.valueAt(i));
+        }
+    }
+
+    /**
      * Flatten a Bundle into the parcel at the current dataPosition(),
      * growing dataCapacity() if needed.
      */
@@ -2258,6 +2276,16 @@
         }
     }
 
+    /* package */ void readArrayMapInternal(ArrayMap outVal, int N,
+        ClassLoader loader) {
+        while (N > 0) {
+            Object key = readValue(loader);
+            Object value = readValue(loader);
+            outVal.append(key, value);
+            N--;
+        }
+    }
+
     private void readListInternal(List outVal, int N,
         ClassLoader loader) {
         while (N > 0) {
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/os/StatFs.java b/core/java/android/os/StatFs.java
index 2314057..9e9521a 100644
--- a/core/java/android/os/StatFs.java
+++ b/core/java/android/os/StatFs.java
@@ -18,14 +18,14 @@
 
 import libcore.io.ErrnoException;
 import libcore.io.Libcore;
-import libcore.io.StructStatFs;
+import libcore.io.StructStatVfs;
 
 /**
  * Retrieve overall information about the space on a filesystem. This is a
- * wrapper for Unix statfs().
+ * wrapper for Unix statvfs().
  */
 public class StatFs {
-    private StructStatFs mStat;
+    private StructStatVfs mStat;
 
     /**
      * Construct a new StatFs for looking at the stats of the filesystem at
@@ -39,9 +39,9 @@
         mStat = doStat(path);
     }
 
-    private static StructStatFs doStat(String path) {
+    private static StructStatVfs doStat(String path) {
         try {
-            return Libcore.os.statfs(path);
+            return Libcore.os.statvfs(path);
         } catch (ErrnoException e) {
             throw new IllegalArgumentException("Invalid path: " + path, e);
         }
@@ -66,7 +66,7 @@
 
     /**
      * The size, in bytes, of a block on the file system. This corresponds to
-     * the Unix {@code statfs.f_bsize} field.
+     * the Unix {@code statvfs.f_bsize} field.
      */
     public long getBlockSizeLong() {
         return mStat.f_bsize;
@@ -82,7 +82,7 @@
 
     /**
      * The total number of blocks on the file system. This corresponds to the
-     * Unix {@code statfs.f_blocks} field.
+     * Unix {@code statvfs.f_blocks} field.
      */
     public long getBlockCountLong() {
         return mStat.f_blocks;
@@ -99,7 +99,7 @@
     /**
      * The total number of blocks that are free on the file system, including
      * reserved blocks (that are not available to normal applications). This
-     * corresponds to the Unix {@code statfs.f_bfree} field. Most applications
+     * corresponds to the Unix {@code statvfs.f_bfree} field. Most applications
      * will want to use {@link #getAvailableBlocks()} instead.
      */
     public long getFreeBlocksLong() {
@@ -125,7 +125,7 @@
 
     /**
      * The number of blocks that are free on the file system and available to
-     * applications. This corresponds to the Unix {@code statfs.f_bavail} field.
+     * applications. This corresponds to the Unix {@code statvfs.f_bavail} field.
      */
     public long getAvailableBlocksLong() {
         return mStat.f_bavail;
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index c33a28a..cdaa868 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -678,4 +678,13 @@
         }
         return false;
     }
+
+    /** @hide */
+    public void removeRestrictions() {
+        try {
+            mService.removeRestrictions();
+        } catch (RemoteException re) {
+            Log.w(TAG, "Could not change restrictions pin");
+        }
+    }
 }
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 028317f..ec97efb 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -901,6 +901,8 @@
 
     @Override
     protected void onDestroy() {
+        mHandler.removeMessages(MSG_BIND_PREFERENCES);
+        mHandler.removeMessages(MSG_BUILD_HEADERS);
         super.onDestroy();
 
         if (mPreferenceManager != null) {
@@ -971,6 +973,9 @@
 
     @Override
     protected void onListItemClick(ListView l, View v, int position, long id) {
+        if (!isResumed()) {
+            return;
+        }
         super.onListItemClick(l, v, position, id);
 
         if (mAdapter != null) {
diff --git a/core/java/android/preference/VolumePreference.java b/core/java/android/preference/VolumePreference.java
index caf55d7..dc683a6 100644
--- a/core/java/android/preference/VolumePreference.java
+++ b/core/java/android/preference/VolumePreference.java
@@ -25,11 +25,14 @@
 import android.media.RingtoneManager;
 import android.net.Uri;
 import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Message;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.provider.Settings;
 import android.provider.Settings.System;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.SeekBar;
@@ -115,7 +118,7 @@
 
     public void onActivityStop() {
         if (mSeekBarVolumizer != null) {
-            mSeekBarVolumizer.stopSample();
+            mSeekBarVolumizer.postStopSample();
         }
     }
 
@@ -220,10 +223,10 @@
     /**
      * Turns a {@link SeekBar} into a volume control.
      */
-    public class SeekBarVolumizer implements OnSeekBarChangeListener, Runnable {
+    public class SeekBarVolumizer implements OnSeekBarChangeListener, Handler.Callback {
 
         private Context mContext;
-        private Handler mHandler = new Handler();
+        private Handler mHandler;
 
         private AudioManager mAudioManager;
         private int mStreamType;
@@ -234,6 +237,11 @@
         private SeekBar mSeekBar;
         private int mVolumeBeforeMute = -1;
 
+        private static final int MSG_SET_STREAM_VOLUME = 0;
+        private static final int MSG_START_SAMPLE = 1;
+        private static final int MSG_STOP_SAMPLE = 2;
+        private static final int CHECK_RINGTONE_PLAYBACK_DELAY_MS = 1000;
+
         private ContentObserver mVolumeObserver = new ContentObserver(mHandler) {
             @Override
             public void onChange(boolean selfChange) {
@@ -255,6 +263,10 @@
             mStreamType = streamType;
             mSeekBar = seekBar;
 
+            HandlerThread thread = new HandlerThread(TAG + ".CallbackHandler");
+            thread.start();
+            mHandler = new Handler(thread.getLooper(), this);
+
             initSeekBar(seekBar, defaultUri);
         }
 
@@ -285,8 +297,54 @@
             }
         }
 
+        @Override
+        public boolean handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_SET_STREAM_VOLUME:
+                    mAudioManager.setStreamVolume(mStreamType, mLastProgress, 0);
+                    break;
+                case MSG_START_SAMPLE:
+                    onStartSample();
+                    break;
+                case MSG_STOP_SAMPLE:
+                    onStopSample();
+                    break;
+                default:
+                    Log.e(TAG, "invalid SeekBarVolumizer message: "+msg.what);
+            }
+            return true;
+        }
+
+        private void postStartSample() {
+            mHandler.removeMessages(MSG_START_SAMPLE);
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_START_SAMPLE),
+                    isSamplePlaying() ? CHECK_RINGTONE_PLAYBACK_DELAY_MS : 0);
+        }
+
+        private void onStartSample() {
+            if (!isSamplePlaying()) {
+                onSampleStarting(this);
+                if (mRingtone != null) {
+                    mRingtone.play();
+                }
+            }
+        }
+
+        private void postStopSample() {
+            // remove pending delayed start messages
+            mHandler.removeMessages(MSG_START_SAMPLE);
+            mHandler.removeMessages(MSG_STOP_SAMPLE);
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_STOP_SAMPLE));
+        }
+
+        private void onStopSample() {
+            if (mRingtone != null) {
+                mRingtone.stop();
+            }
+        }
+
         public void stop() {
-            stopSample();
+            postStopSample();
             mContext.getContentResolver().unregisterContentObserver(mVolumeObserver);
             mSeekBar.setOnSeekBarChangeListener(null);
         }
@@ -307,21 +365,15 @@
         void postSetVolume(int progress) {
             // Do the volume changing separately to give responsive UI
             mLastProgress = progress;
-            mHandler.removeCallbacks(this);
-            mHandler.post(this);
+            mHandler.removeMessages(MSG_SET_STREAM_VOLUME);
+            mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_STREAM_VOLUME));
         }
 
         public void onStartTrackingTouch(SeekBar seekBar) {
         }
 
         public void onStopTrackingTouch(SeekBar seekBar) {
-            if (!isSamplePlaying()) {
-                startSample();
-            }
-        }
-
-        public void run() {
-            mAudioManager.setStreamVolume(mStreamType, mLastProgress, 0);
+            postStartSample();
         }
 
         public boolean isSamplePlaying() {
@@ -329,16 +381,11 @@
         }
 
         public void startSample() {
-            onSampleStarting(this);
-            if (mRingtone != null) {
-                mRingtone.play();
-            }
+            postStartSample();
         }
 
         public void stopSample() {
-            if (mRingtone != null) {
-                mRingtone.stop();
-            }
+            postStopSample();
         }
 
         public SeekBar getSeekBar() {
@@ -347,23 +394,21 @@
 
         public void changeVolumeBy(int amount) {
             mSeekBar.incrementProgressBy(amount);
-            if (!isSamplePlaying()) {
-                startSample();
-            }
             postSetVolume(mSeekBar.getProgress());
+            postStartSample();
             mVolumeBeforeMute = -1;
         }
 
         public void muteVolume() {
             if (mVolumeBeforeMute != -1) {
                 mSeekBar.setProgress(mVolumeBeforeMute);
-                startSample();
                 postSetVolume(mVolumeBeforeMute);
+                postStartSample();
                 mVolumeBeforeMute = -1;
             } else {
                 mVolumeBeforeMute = mSeekBar.getProgress();
                 mSeekBar.setProgress(0);
-                stopSample();
+                postStopSample();
                 postSetVolume(0);
             }
         }
diff --git a/core/java/android/print/FileDocumentAdapter.java b/core/java/android/print/FileDocumentAdapter.java
new file mode 100644
index 0000000..c7011f4
--- /dev/null
+++ b/core/java/android/print/FileDocumentAdapter.java
@@ -0,0 +1,142 @@
+/*
+ * 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.Bundle;
+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,
+            Bundle metadata) {
+        // 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 68%
rename from core/java/android/print/IPrintAdapter.aidl
rename to core/java/android/print/IPrintDocumentAdapter.aidl
index f3ff8c4..04da157 100644
--- a/core/java/android/print/IPrintAdapter.aidl
+++ b/core/java/android/print/IPrintDocumentAdapter.aidl
@@ -16,8 +16,10 @@
 
 package android.print;
 
+import android.os.Bundle;
 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 +28,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, in Bundle metadata);
+    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/PageRange.java b/core/java/android/print/PageRange.java
index 60e6229..9257a04 100644
--- a/core/java/android/print/PageRange.java
+++ b/core/java/android/print/PageRange.java
@@ -42,8 +42,10 @@
      * @throws IllegalArgumentException If start is less than zero.
      * @throws IllegalArgumentException If end is less than zero.
      * @throws IllegalArgumentException If start greater than end.
+     *
+     * @hide
      */
-    PageRange(int start, int end) {
+    public PageRange(int start, int end) {
         if (start < 0) {
             throw new IllegalArgumentException("start cannot be less than zero.");
         }
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..65f1330 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.
@@ -669,18 +1007,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;
         }
 
         /**
@@ -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.
@@ -901,56 +1203,19 @@
          *
          * @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;
+        public CharSequence getLabel() {
+            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..1f83a45
--- /dev/null
+++ b/core/java/android/print/PrintDocumentAdapter.java
@@ -0,0 +1,232 @@
+/*
+ * 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.Bundle;
+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, Bundle)} 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, Bundle)}, 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>
+ * <h3>Implementation</h3>
+ * <p>
+ * The APIs defined in this class are designed to enable doing part or all
+ * of the work on an arbitrary thread. For example, if the printed content
+ * does not depend on the UI state, i.e. on what is shown on the screen, then
+ * you can off load the entire work on a dedicated thread, thus making your
+ * application interactive while the print work is being performed.
+ * </p>
+ * <p>
+ * You can also do work on different threads, for example if you print UI
+ * content, you can handle {@link #onStart()} and {@link #onLayout(PrintAttributes,
+ * PrintAttributes, CancellationSignal, LayoutResultCallback, Bundle)} on
+ * the UI thread (assuming onStart initializes resources needed for layout).
+ * This will ensure that the UI does not change while you are laying out the
+ * printed content. Then you can handle {@link #onWrite(List, FileDescriptor,
+ * CancellationSignal, WriteResultCallback)} and {@link #onFinish()} on another
+ * thread. This will ensure that the UI is frozen for the minimal amount of
+ * time. Also this assumes that you will generate the printed content in
+ * {@link #onLayout(PrintAttributes, PrintAttributes, CancellationSignal,
+ * LayoutResultCallback, Bundle)} which is not mandatory. If you use multiple
+ * threads, you are responsible for proper synchronization.
+ * </p>
+ */
+public abstract class PrintDocumentAdapter {
+
+    /**
+     * Meta-data key: mapped to a boolean value that is <code>true</code> if
+     * the current layout is for a print preview, <code>false</code> otherwise.
+     */
+    public static final String METADATA_KEY_PRINT_PREVIEW = "KEY_METADATA_PRINT_PREVIEW";
+
+    /**
+     * 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 <strong>must</strong> 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.
+     * @param metadata Additional information about how layout the content.
+     *
+     * @see LayoutResultCallback
+     * @see CancellationSignal
+     * @see #METADATA_KEY_PRINT_PREVIEW
+     */
+    public abstract void onLayout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+            CancellationSignal cancellationSignal, LayoutResultCallback callback,
+            Bundle metadata);
+
+    /**
+     * 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(List)}, if writing completed successfully; or {@link
+     * WriteResultCallback#onWriteFailed(CharSequence)}, if an error occurred.
+     * </p>
+     * <p>
+     * <strong>Note:</strong> If the printed content is large, it is a good
+     * 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, Bundle)}.
+     */
+    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..7d42b3a
--- /dev/null
+++ b/core/java/android/print/PrintDocumentInfo.java
@@ -0,0 +1,181 @@
+/*
+ * 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);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append("PrintDocumentInfo{");
+        builder.append("pageCount: ").append(mPageCount);
+        builder.append(", contentType: ").append(mContentType);
+        builder.append("}");
+        return builder.toString();
+    }
+
+    /**
+     * 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..97384d9 100644
--- a/core/java/android/print/PrintJobInfo.java
+++ b/core/java/android/print/PrintJobInfo.java
@@ -35,11 +35,20 @@
     public static final int STATE_ANY = -1;
 
     /**
+     * Constant for matching any print job state.
+     *
+     * @hide
+     */
+    public static final int STATE_ANY_VISIBLE_TO_CLIENTS = -2;
+
+    /**
      * Print job state: The print job is being created but not yet
      * ready to be printed.
      * <p>
      * Next valid states: {@link #STATE_QUEUED}
      * </p>
+     *
+     * @hide
      */
     public static final int STATE_CREATED = 1;
 
@@ -116,6 +125,46 @@
     /** 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;
+        mTag = other.mTag;
+        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();
+        mTag = parcel.readString();
+        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 +349,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
@@ -344,6 +384,7 @@
         parcel.writeInt(mState);
         parcel.writeInt(mAppId);
         parcel.writeInt(mUserId);
+        parcel.writeString(mTag);
         if (mPageRanges != null) {
             parcel.writeInt(1);
             parcel.writeParcelableArray(mPageRanges, flags);
@@ -356,6 +397,12 @@
         } else {
             parcel.writeInt(0);
         }
+        if (mDocumentInfo != null) {
+            parcel.writeInt(1);
+            mDocumentInfo.writeToParcel(parcel, flags);
+        } else {
+            parcel.writeInt(0);
+        }
     }
 
     @Override
@@ -366,7 +413,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..f9f53f6 100644
--- a/core/java/android/print/PrintManager.java
+++ b/core/java/android/print/PrintManager.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.content.IntentSender;
 import android.content.IntentSender.SendIntentException;
+import android.os.Bundle;
 import android.os.CancellationSignal;
 import android.os.Handler;
 import android.os.ICancellationSignal;
@@ -26,7 +27,9 @@
 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.text.TextUtils;
 import android.util.Log;
 
 import com.android.internal.os.SomeArgs;
@@ -103,7 +106,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 +115,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 +133,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 +144,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 +168,27 @@
      * @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,
+        if (TextUtils.isEmpty(printJobName)) {
+            throw new IllegalArgumentException("priintJobName cannot be empty");
+        }
+        PrintDocumentAdapterDelegate delegate = new PrintDocumentAdapterDelegate(documentAdapter,
                 mContext.getMainLooper());
         try {
             PrintJobInfo printJob = mService.print(printJobName, mPrintClient, delegate,
@@ -217,145 +222,120 @@
         }
     }
 
-    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, Bundle metadata) {
+            SomeArgs args = SomeArgs.obtain();
+            args.arg1 = oldAttributes;
+            args.arg2 = newAttributes;
+            args.arg3 = callback;
+            args.arg4 = metadata;
+            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;
+                        Bundle metadata = (Bundle) args.arg4;
                         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), metadata);
+                        } 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 +347,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..e884026 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;
     }
 
@@ -125,26 +125,6 @@
         return builder.toString();
     }
 
-    /**
-     * @hide
-     */
-    public String flattenToString() {
-        return mServiceComponentName.flattenToString() + ":" + mLocalId;
-    }
-
-    /**
-     * @hide
-     */
-    public static PrinterId unflattenFromString(String string) {
-        String[] split = string.split(":");
-        if (split.length != 2) {
-            throw new IllegalArgumentException("Not well-formed printer id:" + string);
-        }
-        ComponentName component = ComponentName.unflattenFromString(split[0]);
-        String localId = String.valueOf(split[1]);
-        return new PrinterId(component, localId);
-    }
-
     public static final Parcelable.Creator<PrinterId> CREATOR =
             new Creator<PrinterId>() {
         @Override
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/print/pdf/PdfDocument.java b/core/java/android/print/pdf/PdfDocument.java
index 7ce036d..cfeb975 100644
--- a/core/java/android/print/pdf/PdfDocument.java
+++ b/core/java/android/print/pdf/PdfDocument.java
@@ -56,7 +56,7 @@
  * // finish the page
  * document.finishPage(page);
  * . . .
- * add more pages
+ * // add more pages
  * . . .
  * // write the document content
  * document.writeTo(getOutputStream());
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..dde31d2 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,26 @@
      * 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()
      */
     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);
-                }
+            observer = mDiscoveryObserver;
+        }
+        if (observer != null) {
+            try {
+                observer.addDiscoveredPrinters(printers);
+            } catch (RemoteException re) {
+                Log.e(LOG_TAG, "Error adding discovered printers", re);
             }
         }
     }
@@ -269,37 +272,35 @@
      * 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()
      */
     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);
-                }
+            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 +311,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.
      *
@@ -326,32 +328,32 @@
      * Gets the print jobs for the printers managed by this service.
      *
      * @return The print jobs.
-     *
-     * @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);
-            }
+            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 +377,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,14 +388,15 @@
             }
 
             @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();
             }
         };
     }
@@ -414,26 +418,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 +445,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/provider/CalendarContract.java b/core/java/android/provider/CalendarContract.java
index 25af209..d743484 100644
--- a/core/java/android/provider/CalendarContract.java
+++ b/core/java/android/provider/CalendarContract.java
@@ -2393,7 +2393,7 @@
             intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime));
             intent.putExtra(ALARM_TIME, alarmTime);
             PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
-            manager.set(AlarmManager.RTC_WAKEUP, alarmTime, pi);
+            manager.setExact(AlarmManager.RTC_WAKEUP, alarmTime, pi);
         }
 
         /**
diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java
index 26bcce4..22f62d70 100644
--- a/core/java/android/util/ArrayMap.java
+++ b/core/java/android/util/ArrayMap.java
@@ -34,8 +34,7 @@
  * that may contain large numbers of items.  It is generally slower than a traditional
  * HashMap, since lookups require a binary search and adds and removes require inserting
  * and deleting entries in the array.  For containers holding up to hundreds of items,
- * the performance difference is not significant, less than 50%.  For larger numbers of items
- * this data structure should be avoided.</p>
+ * the performance difference is not significant, less than 50%.</p>
  *
  * <p><b>Note:</b> unlike {@link java.util.HashMap}, this container does not support
  * null keys.</p>
@@ -44,9 +43,7 @@
  * standard Java containers it will shrink its array as items are removed from it.  Currently
  * you have no control over this shrinking -- if you set a capacity and then remove an
  * item, it may reduce the capacity to better match the current size.  In the future an
- * explicitly call to set the capacity should turn off this aggressive shrinking behavior.</p>
- *
- * @hide
+ * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>
  */
 public final class ArrayMap<K, V> implements Map<K, V> {
     private static final boolean DEBUG = false;
@@ -64,6 +61,11 @@
     private static final int CACHE_SIZE = 10;
 
     /**
+     * @hide Special immutable empty ArrayMap.
+     */
+    public static final ArrayMap EMPTY = new ArrayMap(true);
+
+    /**
      * Caches of small array objects to avoid spamming garbage.  The cache
      * Object[] variable is a pointer to a linked list of array objects.
      * The first entry in the array is a pointer to the next array in the
@@ -74,6 +76,11 @@
     static Object[] mTwiceBaseCache;
     static int mTwiceBaseCacheSize;
 
+    /**
+     * Special hash array value that indicates the container is immutable.
+     */
+    static final int[] EMPTY_IMMUTABLE_INTS = new int[0];
+
     int[] mHashes;
     Object[] mArray;
     int mSize;
@@ -87,7 +94,7 @@
             return ~0;
         }
 
-        int index = SparseArray.binarySearch(mHashes, N, hash);
+        int index = ContainerHelpers.binarySearch(mHashes, N, hash);
 
         // If the hash code wasn't found, then we have no entry for this key.
         if (index < 0) {
@@ -118,6 +125,9 @@
     }
 
     private void allocArrays(final int size) {
+        if (mHashes == EMPTY_IMMUTABLE_INTS) {
+            throw new UnsupportedOperationException("ArrayMap is immutable");
+        }
         if (size == (BASE_SIZE*2)) {
             synchronized (ArrayMap.class) {
                 if (mTwiceBaseCache != null) {
@@ -189,8 +199,8 @@
      * will grow once items are added to it.
      */
     public ArrayMap() {
-        mHashes = SparseArray.EMPTY_INTS;
-        mArray = SparseArray.EMPTY_OBJECTS;
+        mHashes = ContainerHelpers.EMPTY_INTS;
+        mArray = ContainerHelpers.EMPTY_OBJECTS;
         mSize = 0;
     }
 
@@ -199,14 +209,20 @@
      */
     public ArrayMap(int capacity) {
         if (capacity == 0) {
-            mHashes = SparseArray.EMPTY_INTS;
-            mArray = SparseArray.EMPTY_OBJECTS;
+            mHashes = ContainerHelpers.EMPTY_INTS;
+            mArray = ContainerHelpers.EMPTY_OBJECTS;
         } else {
             allocArrays(capacity);
         }
         mSize = 0;
     }
 
+    private ArrayMap(boolean immutable) {
+        mHashes = EMPTY_IMMUTABLE_INTS;
+        mArray = ContainerHelpers.EMPTY_OBJECTS;
+        mSize = 0;
+    }
+
     /**
      * Create a new ArrayMap with the mappings from the given ArrayMap.
      */
@@ -222,15 +238,29 @@
      */
     @Override
     public void clear() {
-        if (mSize != 0) {
+        if (mSize > 0) {
             freeArrays(mHashes, mArray, mSize);
-            mHashes = SparseArray.EMPTY_INTS;
-            mArray = SparseArray.EMPTY_OBJECTS;
+            mHashes = ContainerHelpers.EMPTY_INTS;
+            mArray = ContainerHelpers.EMPTY_OBJECTS;
             mSize = 0;
         }
     }
 
     /**
+     * @hide
+     * Like {@link #clear}, but doesn't reduce the capacity of the ArrayMap.
+     */
+    public void erase() {
+        if (mSize > 0) {
+            final int N = mSize<<1;
+            final Object[] array = mArray;
+            for (int i=0; i<N; i++) {
+                array[i] = null;
+            }
+        }
+    }
+
+    /**
      * Ensure the array map can hold at least <var>minimumCapacity</var>
      * items.
      */
@@ -394,6 +424,33 @@
     }
 
     /**
+     * Special fast path for appending items to the end of the array without validation.
+     * The array must already be large enough to contain the item.
+     * @hide
+     */
+    public void append(K key, V value) {
+        int index = mSize;
+        final int hash = key.hashCode();
+        if (index >= mHashes.length) {
+            throw new IllegalStateException("Array is full");
+        }
+        if (index > 0 && mHashes[index-1] > hash) {
+            RuntimeException e = new RuntimeException("here");
+            e.fillInStackTrace();
+            Log.w(TAG, "New hash " + hash
+                    + " is before end of array hash " + mHashes[index-1]
+                    + " at index " + index + " key " + key, e);
+            put(key, value);
+            return;
+        }
+        mSize = index+1;
+        mHashes[index] = hash;
+        index <<= 1;
+        mArray[index] = key;
+        mArray[index+1] = value;
+    }
+
+    /**
      * Perform a {@link #put(Object, Object)} of all key/value pairs in <var>array</var>
      * @param array The array whose contents are to be retrieved.
      */
@@ -440,8 +497,8 @@
             // Now empty.
             if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
             freeArrays(mHashes, mArray, mSize);
-            mHashes = SparseArray.EMPTY_INTS;
-            mArray = SparseArray.EMPTY_OBJECTS;
+            mHashes = ContainerHelpers.EMPTY_INTS;
+            mArray = ContainerHelpers.EMPTY_OBJECTS;
             mSize = 0;
         } else {
             if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index 2d3380e..1648ec9 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -33,8 +33,7 @@
  * that may contain large numbers of items.  It is generally slower than a traditional
  * HashSet, since lookups require a binary search and adds and removes require inserting
  * and deleting entries in the array.  For containers holding up to hundreds of items,
- * the performance difference is not significant, less than 50%.  For larger numbers of items
- * this data structure should be avoided.</p>
+ * the performance difference is not significant, less than 50%.</p>
  *
  * <p><b>Note:</b> unlike {@link java.util.HashSet}, this container does not support
  * null values.</p>
@@ -43,7 +42,7 @@
  * standard Java containers it will shrink its array as items are removed from it.  Currently
  * you have no control over this shrinking -- if you set a capacity and then remove an
  * item, it may reduce the capacity to better match the current size.  In the future an
- * explicitly call to set the capacity should turn off this aggressive shrinking behavior.</p>
+ * explicit call to set the capacity should turn off this aggressive shrinking behavior.</p>
  *
  * @hide
  */
@@ -86,7 +85,7 @@
             return ~0;
         }
 
-        int index = SparseArray.binarySearch(mHashes, N, hash);
+        int index = ContainerHelpers.binarySearch(mHashes, N, hash);
 
         // If the hash code wasn't found, then we have no entry for this key.
         if (index < 0) {
@@ -188,8 +187,8 @@
      * will grow once items are added to it.
      */
     public ArraySet() {
-        mHashes = SparseArray.EMPTY_INTS;
-        mArray = SparseArray.EMPTY_OBJECTS;
+        mHashes = ContainerHelpers.EMPTY_INTS;
+        mArray = ContainerHelpers.EMPTY_OBJECTS;
         mSize = 0;
     }
 
@@ -198,8 +197,8 @@
      */
     public ArraySet(int capacity) {
         if (capacity == 0) {
-            mHashes = SparseArray.EMPTY_INTS;
-            mArray = SparseArray.EMPTY_OBJECTS;
+            mHashes = ContainerHelpers.EMPTY_INTS;
+            mArray = ContainerHelpers.EMPTY_OBJECTS;
         } else {
             allocArrays(capacity);
         }
@@ -224,8 +223,8 @@
     public void clear() {
         if (mSize != 0) {
             freeArrays(mHashes, mArray, mSize);
-            mHashes = SparseArray.EMPTY_INTS;
-            mArray = SparseArray.EMPTY_OBJECTS;
+            mHashes = ContainerHelpers.EMPTY_INTS;
+            mArray = ContainerHelpers.EMPTY_OBJECTS;
             mSize = 0;
         }
     }
@@ -372,8 +371,8 @@
             // Now empty.
             if (DEBUG) Log.d(TAG, "remove: shrink from " + mHashes.length + " to 0");
             freeArrays(mHashes, mArray, mSize);
-            mHashes = SparseArray.EMPTY_INTS;
-            mArray = SparseArray.EMPTY_OBJECTS;
+            mHashes = ContainerHelpers.EMPTY_INTS;
+            mArray = ContainerHelpers.EMPTY_OBJECTS;
             mSize = 0;
         } else {
             if (mHashes.length > (BASE_SIZE*2) && mSize < mHashes.length/3) {
diff --git a/core/java/android/util/ContainerHelpers.java b/core/java/android/util/ContainerHelpers.java
new file mode 100644
index 0000000..624c4bd
--- /dev/null
+++ b/core/java/android/util/ContainerHelpers.java
@@ -0,0 +1,63 @@
+/*
+ * 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.util;
+
+class ContainerHelpers {
+    static final boolean[] EMPTY_BOOLEANS = new boolean[0];
+    static final int[] EMPTY_INTS = new int[0];
+    static final long[] EMPTY_LONGS = new long[0];
+    static final Object[] EMPTY_OBJECTS = new Object[0];
+
+    // This is Arrays.binarySearch(), but doesn't do any argument validation.
+    static int binarySearch(int[] array, int size, int value) {
+        int lo = 0;
+        int hi = size - 1;
+
+        while (lo <= hi) {
+            final int mid = (lo + hi) >>> 1;
+            final int midVal = array[mid];
+
+            if (midVal < value) {
+                lo = mid + 1;
+            } else if (midVal > value) {
+                hi = mid - 1;
+            } else {
+                return mid;  // value found
+            }
+        }
+        return ~lo;  // value not present
+    }
+
+    static int binarySearch(long[] array, int size, long value) {
+        int lo = 0;
+        int hi = size - 1;
+
+        while (lo <= hi) {
+            final int mid = (lo + hi) >>> 1;
+            final long midVal = array[mid];
+
+            if (midVal < value) {
+                lo = mid + 1;
+            } else if (midVal > value) {
+                hi = mid - 1;
+            } else {
+                return mid;  // value found
+            }
+        }
+        return ~lo;  // value not present
+    }
+}
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
index 660b743..63e8c25 100644
--- a/core/java/android/util/LongSparseArray.java
+++ b/core/java/android/util/LongSparseArray.java
@@ -20,8 +20,25 @@
 
 /**
  * SparseArray mapping longs to Objects.  Unlike a normal array of Objects,
- * there can be gaps in the indices.  It is intended to be more efficient
- * than using a HashMap to map Longs to Objects.
+ * there can be gaps in the indices.  It is intended to be more memory efficient
+ * than using a HashMap to map Longs to Objects, both because it avoids
+ * auto-boxing keys and its data structure doesn't rely on an extra entry object
+ * for each mapping.
+ *
+ * <p>Note that this container keeps its mappings in an array data structure,
+ * using a binary search to find keys.  The implementation is not intended to be appropriate for
+ * data structures
+ * that may contain large numbers of items.  It is generally slower than a traditional
+ * HashMap, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array.  For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%.</p>
+ *
+ * <p>To help with performance, the container includes an optimization when removing
+ * keys: instead of compacting its array immediately, it leaves the removed entry marked
+ * as deleted.  The entry can then be re-used for the same key, or compacted later in
+ * a single garbage collection step of all removed entries.  This garbage collection will
+ * need to be performed at any time the array needs to be grown or the the map size or
+ * entry values are retrieved.</p>
  */
 public class LongSparseArray<E> implements Cloneable {
     private static final Object DELETED = new Object();
@@ -47,8 +64,8 @@
      */
     public LongSparseArray(int initialCapacity) {
         if (initialCapacity == 0) {
-            mKeys = SparseLongArray.EMPTY_LONGS;
-            mValues = SparseArray.EMPTY_OBJECTS;
+            mKeys = ContainerHelpers.EMPTY_LONGS;
+            mValues = ContainerHelpers.EMPTY_OBJECTS;
         } else {
             initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
             mKeys = new long[initialCapacity];
@@ -85,7 +102,7 @@
      */
     @SuppressWarnings("unchecked")
     public E get(long key, E valueIfKeyNotFound) {
-        int i = binarySearch(mKeys, 0, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i < 0 || mValues[i] == DELETED) {
             return valueIfKeyNotFound;
@@ -98,7 +115,7 @@
      * Removes the mapping from the specified key, if there was any.
      */
     public void delete(long key) {
-        int i = binarySearch(mKeys, 0, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             if (mValues[i] != DELETED) {
@@ -159,7 +176,7 @@
      * was one.
      */
     public void put(long key, E value) {
-        int i = binarySearch(mKeys, 0, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             mValues[i] = value;
@@ -176,7 +193,7 @@
                 gc();
 
                 // Search again because indices may have changed.
-                i = ~binarySearch(mKeys, 0, mSize, key);
+                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
             }
 
             if (mSize >= mKeys.length) {
@@ -267,7 +284,7 @@
             gc();
         }
 
-        return binarySearch(mKeys, 0, mSize, key);
+        return ContainerHelpers.binarySearch(mKeys, mSize, key);
     }
 
     /**
@@ -339,23 +356,36 @@
         mSize = pos + 1;
     }
 
-    private static int binarySearch(long[] a, int start, int len, long key) {
-        int high = start + len, low = start - 1, guess;
-
-        while (high - low > 1) {
-            guess = (high + low) / 2;
-
-            if (a[guess] < key)
-                low = guess;
-            else
-                high = guess;
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation composes a string by iterating over its mappings. If
+     * this map contains itself as a value, the string "(this Map)"
+     * will appear in its place.
+     */
+    @Override
+    public String toString() {
+        if (size() <= 0) {
+            return "{}";
         }
 
-        if (high == start + len)
-            return ~(start + len);
-        else if (a[high] == key)
-            return high;
-        else
-            return ~high;
+        StringBuilder buffer = new StringBuilder(mSize * 28);
+        buffer.append('{');
+        for (int i=0; i<mSize; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            long key = keyAt(i);
+            buffer.append(key);
+            buffer.append('=');
+            Object value = valueAt(i);
+            if (value != this) {
+                buffer.append(value);
+            } else {
+                buffer.append("(this Map)");
+            }
+        }
+        buffer.append('}');
+        return buffer.toString();
     }
 }
diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java
index 503295c..133e415 100644
--- a/core/java/android/util/LongSparseLongArray.java
+++ b/core/java/android/util/LongSparseLongArray.java
@@ -22,8 +22,18 @@
 
 /**
  * Map of {@code long} to {@code long}. Unlike a normal array of longs, there
- * can be gaps in the indices. It is intended to be more efficient than using a
- * {@code HashMap}.
+ * can be gaps in the indices. It is intended to be more memory efficient than using a
+ * {@code HashMap}, both because it avoids
+ * auto-boxing keys and values and its data structure doesn't rely on an extra entry object
+ * for each mapping.
+ *
+ * <p>Note that this container keeps its mappings in an array data structure,
+ * using a binary search to find keys.  The implementation is not intended to be appropriate for
+ * data structures
+ * that may contain large numbers of items.  It is generally slower than a traditional
+ * HashMap, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array.  For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%.</p>
  *
  * @hide
  */
@@ -48,8 +58,8 @@
      */
     public LongSparseLongArray(int initialCapacity) {
         if (initialCapacity == 0) {
-            mKeys = SparseLongArray.EMPTY_LONGS;
-            mValues = SparseLongArray.EMPTY_LONGS;
+            mKeys = ContainerHelpers.EMPTY_LONGS;
+            mValues = ContainerHelpers.EMPTY_LONGS;
         } else {
             initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
             mKeys = new long[initialCapacity];
@@ -84,7 +94,7 @@
      * if no such mapping has been made.
      */
     public long get(long key, long valueIfKeyNotFound) {
-        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i < 0) {
             return valueIfKeyNotFound;
@@ -97,7 +107,7 @@
      * Removes the mapping from the specified key, if there was any.
      */
     public void delete(long key) {
-        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             removeAt(i);
@@ -119,7 +129,7 @@
      * was one.
      */
     public void put(long key, long value) {
-        int i = Arrays.binarySearch(mKeys, 0, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             mValues[i] = value;
@@ -173,7 +183,7 @@
      * key is not mapped.
      */
     public int indexOfKey(long key) {
-        return Arrays.binarySearch(mKeys, 0, mSize, key);
+        return ContainerHelpers.binarySearch(mKeys, mSize, key);
     }
 
     /**
@@ -231,4 +241,31 @@
         mKeys = nkeys;
         mValues = nvalues;
     }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation composes a string by iterating over its mappings.
+     */
+    @Override
+    public String toString() {
+        if (size() <= 0) {
+            return "{}";
+        }
+
+        StringBuilder buffer = new StringBuilder(mSize * 28);
+        buffer.append('{');
+        for (int i=0; i<mSize; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            long key = keyAt(i);
+            buffer.append(key);
+            buffer.append('=');
+            long value = valueAt(i);
+            buffer.append(value);
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
 }
diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java
index 001fc5b..6e66090 100644
--- a/core/java/android/util/SparseArray.java
+++ b/core/java/android/util/SparseArray.java
@@ -20,13 +20,28 @@
 
 /**
  * SparseArrays map integers to Objects.  Unlike a normal array of Objects,
- * there can be gaps in the indices.  It is intended to be more efficient
- * than using a HashMap to map Integers to Objects.
+ * there can be gaps in the indices.  It is intended to be more memory efficient
+ * than using a HashMap to map Integers to Objects, both because it avoids
+ * auto-boxing keys and its data structure doesn't rely on an extra entry object
+ * for each mapping.
+ *
+ * <p>Note that this container keeps its mappings in an array data structure,
+ * using a binary search to find keys.  The implementation is not intended to be appropriate for
+ * data structures
+ * that may contain large numbers of items.  It is generally slower than a traditional
+ * HashMap, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array.  For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%.</p>
+ *
+ * <p>To help with performance, the container includes an optimization when removing
+ * keys: instead of compacting its array immediately, it leaves the removed entry marked
+ * as deleted.  The entry can then be re-used for the same key, or compacted later in
+ * a single garbage collection step of all removed entries.  This garbage collection will
+ * need to be performed at any time the array needs to be grown or the the map size or
+ * entry values are retrieved.</p>
  */
 public class SparseArray<E> implements Cloneable {
     private static final Object DELETED = new Object();
-    static final int[] EMPTY_INTS = new int[0];
-    static final Object[] EMPTY_OBJECTS = new Object[0];
     private boolean mGarbage = false;
 
     private int[] mKeys;
@@ -49,8 +64,8 @@
      */
     public SparseArray(int initialCapacity) {
         if (initialCapacity == 0) {
-            mKeys = EMPTY_INTS;
-            mValues = EMPTY_OBJECTS;
+            mKeys = ContainerHelpers.EMPTY_INTS;
+            mValues = ContainerHelpers.EMPTY_OBJECTS;
         } else {
             initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
             mKeys = new int[initialCapacity];
@@ -87,7 +102,7 @@
      */
     @SuppressWarnings("unchecked")
     public E get(int key, E valueIfKeyNotFound) {
-        int i = binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i < 0 || mValues[i] == DELETED) {
             return valueIfKeyNotFound;
@@ -100,7 +115,7 @@
      * Removes the mapping from the specified key, if there was any.
      */
     public void delete(int key) {
-        int i = binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             if (mValues[i] != DELETED) {
@@ -127,6 +142,19 @@
         }
     }
 
+    /**
+     * Remove a range of mappings as a batch.
+     *
+     * @param index Index to begin at
+     * @param size Number of mappings to remove
+     */
+    public void removeAtRange(int index, int size) {
+        final int end = Math.min(mSize, index + size);
+        for (int i = index; i < end; i++) {
+            removeAt(i);
+        }
+    }
+
     private void gc() {
         // Log.e("SparseArray", "gc start with " + mSize);
 
@@ -161,7 +189,7 @@
      * was one.
      */
     public void put(int key, E value) {
-        int i = binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             mValues[i] = value;
@@ -178,7 +206,7 @@
                 gc();
 
                 // Search again because indices may have changed.
-                i = ~binarySearch(mKeys, mSize, key);
+                i = ~ContainerHelpers.binarySearch(mKeys, mSize, key);
             }
 
             if (mSize >= mKeys.length) {
@@ -269,7 +297,7 @@
             gc();
         }
 
-        return binarySearch(mKeys, mSize, key);
+        return ContainerHelpers.binarySearch(mKeys, mSize, key);
     }
 
     /**
@@ -343,23 +371,36 @@
         mSize = pos + 1;
     }
 
-    // This is Arrays.binarySearch(), but doesn't do any argument validation.
-    static int binarySearch(int[] array, int size, int value) {
-        int lo = 0;
-        int hi = size - 1;
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation composes a string by iterating over its mappings. If
+     * this map contains itself as a value, the string "(this Map)"
+     * will appear in its place.
+     */
+    @Override
+    public String toString() {
+        if (size() <= 0) {
+            return "{}";
+        }
 
-        while (lo <= hi) {
-            int mid = (lo + hi) >>> 1;
-            int midVal = array[mid];
-
-            if (midVal < value) {
-                lo = mid + 1;
-            } else if (midVal > value) {
-                hi = mid - 1;
+        StringBuilder buffer = new StringBuilder(mSize * 28);
+        buffer.append('{');
+        for (int i=0; i<mSize; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            int key = keyAt(i);
+            buffer.append(key);
+            buffer.append('=');
+            Object value = valueAt(i);
+            if (value != this) {
+                buffer.append(value);
             } else {
-                return mid;  // value found
+                buffer.append("(this Map)");
             }
         }
-        return ~lo;  // value not present
+        buffer.append('}');
+        return buffer.toString();
     }
 }
diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java
index 73e3629..da196d7 100644
--- a/core/java/android/util/SparseBooleanArray.java
+++ b/core/java/android/util/SparseBooleanArray.java
@@ -21,12 +21,20 @@
 /**
  * SparseBooleanArrays map integers to booleans.
  * Unlike a normal array of booleans
- * there can be gaps in the indices.  It is intended to be more efficient
- * than using a HashMap to map Integers to Booleans.
+ * there can be gaps in the indices.  It is intended to be more memory efficient
+ * than using a HashMap to map Integers to Booleans, both because it avoids
+ * auto-boxing keys and values and its data structure doesn't rely on an extra entry object
+ * for each mapping.
+ *
+ * <p>Note that this container keeps its mappings in an array data structure,
+ * using a binary search to find keys.  The implementation is not intended to be appropriate for
+ * data structures
+ * that may contain large numbers of items.  It is generally slower than a traditional
+ * HashMap, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array.  For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%.</p>
  */
 public class SparseBooleanArray implements Cloneable {
-    static final boolean[] EMPTY_BOOLEANS = new boolean[0];
-
     /**
      * Creates a new SparseBooleanArray containing no mappings.
      */
@@ -43,8 +51,8 @@
      */
     public SparseBooleanArray(int initialCapacity) {
         if (initialCapacity == 0) {
-            mKeys = SparseArray.EMPTY_INTS;
-            mValues = EMPTY_BOOLEANS;
+            mKeys = ContainerHelpers.EMPTY_INTS;
+            mValues = ContainerHelpers.EMPTY_BOOLEANS;
         } else {
             initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
             mKeys = new int[initialCapacity];
@@ -79,7 +87,7 @@
      * if no such mapping has been made.
      */
     public boolean get(int key, boolean valueIfKeyNotFound) {
-        int i = SparseArray.binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i < 0) {
             return valueIfKeyNotFound;
@@ -92,7 +100,7 @@
      * Removes the mapping from the specified key, if there was any.
      */
     public void delete(int key) {
-        int i = SparseArray.binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             System.arraycopy(mKeys, i + 1, mKeys, i, mSize - (i + 1));
@@ -107,7 +115,7 @@
      * was one.
      */
     public void put(int key, boolean value) {
-        int i = SparseArray.binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             mValues[i] = value;
@@ -172,7 +180,7 @@
      * key is not mapped.
      */
     public int indexOfKey(int key) {
-        return SparseArray.binarySearch(mKeys, mSize, key);
+        return ContainerHelpers.binarySearch(mKeys, mSize, key);
     }
 
     /**
@@ -227,7 +235,34 @@
         mValues[pos] = value;
         mSize = pos + 1;
     }
-    
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation composes a string by iterating over its mappings.
+     */
+    @Override
+    public String toString() {
+        if (size() <= 0) {
+            return "{}";
+        }
+
+        StringBuilder buffer = new StringBuilder(mSize * 28);
+        buffer.append('{');
+        for (int i=0; i<mSize; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            int key = keyAt(i);
+            buffer.append(key);
+            buffer.append('=');
+            boolean value = valueAt(i);
+            buffer.append(value);
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
+
     private int[] mKeys;
     private boolean[] mValues;
     private int mSize;
diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java
index 122f7f5..c2bacb0 100644
--- a/core/java/android/util/SparseIntArray.java
+++ b/core/java/android/util/SparseIntArray.java
@@ -20,8 +20,18 @@
 
 /**
  * SparseIntArrays map integers to integers.  Unlike a normal array of integers,
- * there can be gaps in the indices.  It is intended to be more efficient
- * than using a HashMap to map Integers to Integers.
+ * there can be gaps in the indices.  It is intended to be more memory efficient
+ * than using a HashMap to map Integers to Integers, both because it avoids
+ * auto-boxing keys and values and its data structure doesn't rely on an extra entry object
+ * for each mapping.
+ *
+ * <p>Note that this container keeps its mappings in an array data structure,
+ * using a binary search to find keys.  The implementation is not intended to be appropriate for
+ * data structures
+ * that may contain large numbers of items.  It is generally slower than a traditional
+ * HashMap, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array.  For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%.</p>
  */
 public class SparseIntArray implements Cloneable {
     private int[] mKeys;
@@ -44,8 +54,8 @@
      */
     public SparseIntArray(int initialCapacity) {
         if (initialCapacity == 0) {
-            mKeys = SparseArray.EMPTY_INTS;
-            mValues = SparseArray.EMPTY_INTS;
+            mKeys = ContainerHelpers.EMPTY_INTS;
+            mValues = ContainerHelpers.EMPTY_INTS;
         } else {
             initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
             mKeys = new int[initialCapacity];
@@ -80,7 +90,7 @@
      * if no such mapping has been made.
      */
     public int get(int key, int valueIfKeyNotFound) {
-        int i = SparseArray.binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i < 0) {
             return valueIfKeyNotFound;
@@ -93,7 +103,7 @@
      * Removes the mapping from the specified key, if there was any.
      */
     public void delete(int key) {
-        int i = SparseArray.binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             removeAt(i);
@@ -115,7 +125,7 @@
      * was one.
      */
     public void put(int key, int value) {
-        int i = SparseArray.binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             mValues[i] = value;
@@ -180,7 +190,7 @@
      * key is not mapped.
      */
     public int indexOfKey(int key) {
-        return SparseArray.binarySearch(mKeys, mSize, key);
+        return ContainerHelpers.binarySearch(mKeys, mSize, key);
     }
 
     /**
@@ -235,4 +245,31 @@
         mValues[pos] = value;
         mSize = pos + 1;
     }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation composes a string by iterating over its mappings.
+     */
+    @Override
+    public String toString() {
+        if (size() <= 0) {
+            return "{}";
+        }
+
+        StringBuilder buffer = new StringBuilder(mSize * 28);
+        buffer.append('{');
+        for (int i=0; i<mSize; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            int key = keyAt(i);
+            buffer.append(key);
+            buffer.append('=');
+            int value = valueAt(i);
+            buffer.append(value);
+        }
+        buffer.append('}');
+        return buffer.toString();
+    }
 }
diff --git a/core/java/android/util/SparseLongArray.java b/core/java/android/util/SparseLongArray.java
index c608996..182fd35 100644
--- a/core/java/android/util/SparseLongArray.java
+++ b/core/java/android/util/SparseLongArray.java
@@ -20,12 +20,20 @@
 
 /**
  * SparseLongArrays map integers to longs.  Unlike a normal array of longs,
- * there can be gaps in the indices.  It is intended to be more efficient
- * than using a HashMap to map Integers to Longs.
+ * there can be gaps in the indices.  It is intended to be more memory efficient
+ * than using a HashMap to map Integers to Longs, both because it avoids
+ * auto-boxing keys and values and its data structure doesn't rely on an extra entry object
+ * for each mapping.
+ *
+ * <p>Note that this container keeps its mappings in an array data structure,
+ * using a binary search to find keys.  The implementation is not intended to be appropriate for
+ * data structures
+ * that may contain large numbers of items.  It is generally slower than a traditional
+ * HashMap, since lookups require a binary search and adds and removes require inserting
+ * and deleting entries in the array.  For containers holding up to hundreds of items,
+ * the performance difference is not significant, less than 50%.</p>
  */
 public class SparseLongArray implements Cloneable {
-    static final long[] EMPTY_LONGS = new long[0];
-
     private int[] mKeys;
     private long[] mValues;
     private int mSize;
@@ -46,8 +54,8 @@
      */
     public SparseLongArray(int initialCapacity) {
         if (initialCapacity == 0) {
-            mKeys = SparseArray.EMPTY_INTS;
-            mValues = EMPTY_LONGS;
+            mKeys = ContainerHelpers.EMPTY_INTS;
+            mValues = ContainerHelpers.EMPTY_LONGS;
         } else {
             initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
             mKeys = new int[initialCapacity];
@@ -82,7 +90,7 @@
      * if no such mapping has been made.
      */
     public long get(int key, long valueIfKeyNotFound) {
-        int i = SparseArray.binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i < 0) {
             return valueIfKeyNotFound;
@@ -95,7 +103,7 @@
      * Removes the mapping from the specified key, if there was any.
      */
     public void delete(int key) {
-        int i = SparseArray.binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             removeAt(i);
@@ -117,7 +125,7 @@
      * was one.
      */
     public void put(int key, long value) {
-        int i = SparseArray.binarySearch(mKeys, mSize, key);
+        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
 
         if (i >= 0) {
             mValues[i] = value;
@@ -171,7 +179,7 @@
      * key is not mapped.
      */
     public int indexOfKey(int key) {
-        return SparseArray.binarySearch(mKeys, mSize, key);
+        return ContainerHelpers.binarySearch(mKeys, mSize, key);
     }
 
     /**
@@ -229,4 +237,31 @@
         mKeys = nkeys;
         mValues = nvalues;
     }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>This implementation composes a string by iterating over its mappings.
+     */
+    @Override
+    public String toString() {
+        if (size() <= 0) {
+            return "{}";
+        }
+
+        StringBuilder buffer = new StringBuilder(mSize * 28);
+        buffer.append('{');
+        for (int i=0; i<mSize; i++) {
+            if (i > 0) {
+                buffer.append(", ");
+            }
+            int key = keyAt(i);
+            buffer.append(key);
+            buffer.append('=');
+            long value = valueAt(i);
+            buffer.append(value);
+        }
+        buffer.append('}');
+        return buffer.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/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java
index a4cc630..bb5a6eb 100644
--- a/core/java/android/view/GLES20TextureLayer.java
+++ b/core/java/android/view/GLES20TextureLayer.java
@@ -73,7 +73,7 @@
 
     SurfaceTexture getSurfaceTexture() {
         if (mSurface == null) {
-            mSurface = new SurfaceTexture(mTexture, false);
+            mSurface = new SurfaceTexture(mTexture);
         }
         return mSurface;
     }
diff --git a/core/java/android/view/GraphicBuffer.java b/core/java/android/view/GraphicBuffer.java
index b4576f3..30c077c 100644
--- a/core/java/android/view/GraphicBuffer.java
+++ b/core/java/android/view/GraphicBuffer.java
@@ -62,6 +62,9 @@
     private Canvas mCanvas;
     private int mSaveCount;
 
+    // If set to true, this GraphicBuffer instance cannot be used anymore
+    private boolean mDestroyed;
+
     /**
      * Creates new <code>GraphicBuffer</code> instance. This method will return null
      * if the buffer cannot be created.
@@ -128,10 +131,14 @@
      * <p>The content of the buffer is preserved between unlockCanvas()
      * and lockCanvas().</p>
      *
+     * <p>If this method is called after {@link #destroy()}, the return value will
+     * always be null.</p>
+     *
      * @return A Canvas used to draw into the buffer, or null.
      *
      * @see #lockCanvas(android.graphics.Rect)
      * @see #unlockCanvasAndPost(android.graphics.Canvas)
+     * @see #isDestroyed()
      */
     public Canvas lockCanvas() {
         return lockCanvas(null);
@@ -141,14 +148,22 @@
      * Just like {@link #lockCanvas()} but allows specification of a dirty
      * rectangle.
      *
+     * <p>If this method is called after {@link #destroy()}, the return value will
+     * always be null.</p>
+     *
      * @param dirty Area of the buffer that may be modified.
 
-     * @return A Canvas used to draw into the surface or null
+     * @return A Canvas used to draw into the surface, or null.
      *
      * @see #lockCanvas()
      * @see #unlockCanvasAndPost(android.graphics.Canvas)
+     * @see #isDestroyed()
      */
     public Canvas lockCanvas(Rect dirty) {
+        if (mDestroyed) {
+            return null;
+        }
+
         if (mCanvas == null) {
             mCanvas = new Canvas();
         }
@@ -164,13 +179,17 @@
     /**
      * Finish editing pixels in the buffer.
      *
+     * <p>This method doesn't do anything if {@link #destroy()} was
+     * previously called.</p>
+     *
      * @param canvas The Canvas previously returned by lockCanvas()
      *
      * @see #lockCanvas()
      * @see #lockCanvas(android.graphics.Rect)
+     * @see #isDestroyed()
      */
     public void unlockCanvasAndPost(Canvas canvas) {
-        if (mCanvas != null && canvas == mCanvas) {
+        if (!mDestroyed && mCanvas != null && canvas == mCanvas) {
             canvas.restoreToCount(mSaveCount);
             mSaveCount = 0;
 
@@ -178,10 +197,39 @@
         }
     }
 
+    /**
+     * Destroyes this buffer immediately. Calling this method frees up any
+     * underlying native resources. After calling this method, this buffer
+     * must not be used in any way ({@link #lockCanvas()} must not be called,
+     * etc.)
+     *
+     * @see #isDestroyed()
+     */
+    public void destroy() {
+        if (!mDestroyed) {
+            mDestroyed = true;
+            nDestroyGraphicBuffer(mNativeObject);
+        }
+    }
+
+    /**
+     * Indicates whether this buffer has been destroyed. A destroyed buffer
+     * cannot be used in any way: locking a Canvas will return null, the buffer
+     * cannot be written to a parcel, etc.
+     *
+     * @return True if this <code>GraphicBuffer</code> is in a destroyed state,
+     *         false otherwise.
+     *
+     * @see #destroy()
+     */
+    public boolean isDestroyed() {
+        return mDestroyed;
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
-            nDestroyGraphicBuffer(mNativeObject);
+            if (!mDestroyed) nDestroyGraphicBuffer(mNativeObject);
         } finally {
             super.finalize();
         }
@@ -192,8 +240,23 @@
         return 0;
     }
 
+    /**
+     * Flatten this object in to a Parcel.
+     *
+     * <p>Calling this method will throw an <code>IllegalStateException</code> if
+     * {@link #destroy()} has been previously called.</p>
+     *
+     * @param dest The Parcel in which the object should be written.
+     * @param flags Additional flags about how the object should be written.
+     *              May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}.
+     */
     @Override
     public void writeToParcel(Parcel dest, int flags) {
+        if (mDestroyed) {
+            throw new IllegalStateException("This GraphicBuffer has been destroyed and cannot be "
+                    + "written to a parcel.");
+        }
+
         dest.writeInt(mWidth);
         dest.writeInt(mHeight);
         dest.writeInt(mFormat);
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 281bd7e..d9e4600 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -1983,6 +1983,7 @@
                         if (map != null) {
                             GLES20Canvas.initAtlas(buffer, map);
                         }
+                        buffer.destroy();
                     }
                 }
             } catch (RemoteException e) {
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 0289407..aea2799c 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -78,7 +78,7 @@
     void addWindowToken(IBinder token, int type);
     void removeWindowToken(IBinder token);
     void addAppToken(int addPos, IApplicationToken token, int groupId, int stackId,
-            int requestedOrientation, boolean fullscreen, boolean showWhenLocked);
+            int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId);
     void setAppGroupId(IBinder token, int groupId);
     void setAppOrientation(IApplicationToken token, int requestedOrientation);
     int getAppOrientation(IApplicationToken token);
diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java
index 2a761c1..34fa73d 100644
--- a/core/java/android/view/InputDevice.java
+++ b/core/java/android/view/InputDevice.java
@@ -51,6 +51,7 @@
     private final int mKeyboardType;
     private final KeyCharacterMap mKeyCharacterMap;
     private final boolean mHasVibrator;
+    private final boolean mHasButtonUnderPad;
     private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>();
 
     private Vibrator mVibrator; // guarded by mMotionRanges during initialization
@@ -343,7 +344,8 @@
     // Called by native code.
     private InputDevice(int id, int generation, String name, String descriptor,
             boolean isExternal, int sources,
-            int keyboardType, KeyCharacterMap keyCharacterMap, boolean hasVibrator) {
+            int keyboardType, KeyCharacterMap keyCharacterMap,
+            boolean hasVibrator, boolean hasButtonUnderPad) {
         mId = id;
         mGeneration = generation;
         mName = name;
@@ -353,6 +355,7 @@
         mKeyboardType = keyboardType;
         mKeyCharacterMap = keyCharacterMap;
         mHasVibrator = hasVibrator;
+        mHasButtonUnderPad = hasButtonUnderPad;
     }
 
     private InputDevice(Parcel in) {
@@ -365,6 +368,7 @@
         mKeyboardType = in.readInt();
         mKeyCharacterMap = KeyCharacterMap.CREATOR.createFromParcel(in);
         mHasVibrator = in.readInt() != 0;
+        mHasButtonUnderPad = in.readInt() != 0;
 
         for (;;) {
             int axis = in.readInt();
@@ -612,6 +616,15 @@
     }
 
     /**
+     * Reports whether the device has a button under its touchpad
+     * @return Whether the device has a button under its touchpad
+     * @hide
+     */
+    public boolean hasButtonUnderPad() {
+        return mHasButtonUnderPad;
+    }
+
+    /**
      * Provides information about the range of values for a particular {@link MotionEvent} axis.
      *
      * @see InputDevice#getMotionRange(int)
@@ -733,6 +746,7 @@
         out.writeInt(mKeyboardType);
         mKeyCharacterMap.writeToParcel(out, flags);
         out.writeInt(mHasVibrator ? 1 : 0);
+        out.writeInt(mHasButtonUnderPad ? 1 : 0);
 
         final int numRanges = mMotionRanges.size();
         for (int i = 0; i < numRanges; i++) {
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 5db3909..5a5fc10 100644
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -629,8 +629,11 @@
     /** Key code constant: Brightness Up key.
      * Adjusts the screen brightness up. */
     public static final int KEYCODE_BRIGHTNESS_UP   = 221;
+    /** Key code constant: Audio Track key
+     * Switches the audio tracks. */
+    public static final int KEYCODE_MEDIA_AUDIO_TRACK = 222;
 
-    private static final int LAST_KEYCODE           = KEYCODE_BRIGHTNESS_UP;
+    private static final int LAST_KEYCODE           = KEYCODE_MEDIA_AUDIO_TRACK;
 
     // NOTE: If you add a new keycode here you must also add it to:
     //  isSystem()
@@ -874,6 +877,7 @@
         names.append(KEYCODE_ASSIST, "KEYCODE_ASSIST");
         names.append(KEYCODE_BRIGHTNESS_DOWN, "KEYCODE_BRIGHTNESS_DOWN");
         names.append(KEYCODE_BRIGHTNESS_UP, "KEYCODE_BRIGHTNESS_UP");
+        names.append(KEYCODE_MEDIA_AUDIO_TRACK, "KEYCODE_MEDIA_AUDIO_TRACK");
     };
 
     // Symbolic names of all metakeys in bit order from least significant to most significant.
@@ -1833,6 +1837,19 @@
         }
     }
 
+    /** Whether key will, by default, trigger a click on the focused view.
+     * @hide
+     */
+    public static final boolean isConfirmKey(int keyCode) {
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+            case KeyEvent.KEYCODE_ENTER:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     /** {@inheritDoc} */
     @Override
     public final int getDeviceId() {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 1192390..188ddf2 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -52,6 +52,7 @@
 import android.util.AttributeSet;
 import android.util.FloatProperty;
 import android.util.Log;
+import android.util.LongSparseLongArray;
 import android.util.Pools.SynchronizedPool;
 import android.util.Property;
 import android.util.SparseArray;
@@ -1122,7 +1123,6 @@
      *
      * @see android.graphics.drawable.Drawable
      * @see #getDrawableState()
-     * @hide
      */
     protected static final int[] PRESSED_STATE_SET;
     /**
@@ -2198,7 +2198,13 @@
      * Flag indicating that the view has been through at least one layout since it
      * was last attached to a window.
      */
-    static final int PFLAG3_HAS_LAYOUT = 0x4;
+    static final int PFLAG3_IS_LAID_OUT = 0x4;
+
+    /**
+     * Flag indicating that a call to measure() was skipped and should be done
+     * instead when layout() is invoked.
+     */
+    static final int PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT = 0x8;
 
 
     /* End of masks for mPrivateFlags3 */
@@ -2980,6 +2986,8 @@
      */
     int mOldHeightMeasureSpec = Integer.MIN_VALUE;
 
+    private LongSparseLongArray mMeasureCache;
+
     @ViewDebug.ExportedProperty(deepExport = true, prefix = "bg_")
     private Drawable mBackground;
 
@@ -5689,7 +5697,7 @@
      * which you would like to ensure are not being covered.
      *
      * <p>The default implementation of this method simply applies the content
-     * inset's to the view's padding, consuming that content (modifying the
+     * insets to the view's padding, consuming that content (modifying the
      * insets to be 0), and returning true.  This behavior is off by default, but can
      * be enabled through {@link #setFitsSystemWindows(boolean)}.
      *
@@ -5726,8 +5734,8 @@
      * {@link android.os.Build.VERSION_CODES#JELLY_BEAN} you must not modify
      * the insets or else you and Android will be unhappy.
      *
-     * @return Return true if this view applied the insets and it should not
-     * continue propagating further down the hierarchy, false otherwise.
+     * @return {@code true} if this view applied the insets and it should not
+     * continue propagating further down the hierarchy, {@code false} otherwise.
      * @see #getFitsSystemWindows()
      * @see #setFitsSystemWindows(boolean) 
      * @see #setSystemUiVisibility(int)
@@ -5798,15 +5806,15 @@
     }
 
     /**
-     * Check for state of {@link #setFitsSystemWindows(boolean). If this method
-     * returns true, the default implementation of {@link #fitSystemWindows(Rect)}
+     * Check for state of {@link #setFitsSystemWindows(boolean)}. If this method
+     * returns {@code true}, the default implementation of {@link #fitSystemWindows(Rect)}
      * will be executed.
      *
-     * @return Returns true if the default implementation of
+     * @return {@code true} if the default implementation of
      * {@link #fitSystemWindows(Rect)} will be executed.
      *
      * @attr ref android.R.styleable#View_fitsSystemWindows
-     * @see #setFitsSystemWindows()
+     * @see #setFitsSystemWindows(boolean)
      * @see #fitSystemWindows(Rect)
      * @see #setSystemUiVisibility(int)
      */
@@ -6152,8 +6160,8 @@
      * Returns true if this view has been through at least one layout since it
      * was last attached to or detached from a window.
      */
-    public boolean hasLayout() {
-        return (mPrivateFlags3 & PFLAG3_HAS_LAYOUT) == PFLAG3_HAS_LAYOUT;
+    public boolean isLaidOut() {
+        return (mPrivateFlags3 & PFLAG3_IS_LAID_OUT) == PFLAG3_IS_LAID_OUT;
     }
 
     /**
@@ -7051,7 +7059,12 @@
         if ((mPrivateFlags2 & PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED) == 0) {
             mPrivateFlags2 |= PFLAG2_SUBTREE_ACCESSIBILITY_STATE_CHANGED;
             if (mParent != null) {
-                mParent.childAccessibilityStateChanged(this);
+                try {
+                    mParent.childAccessibilityStateChanged(this);
+                } catch (AbstractMethodError e) {
+                    Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                            " does not fully implement ViewParent", e);
+                }
             }
         }
     }
@@ -7932,21 +7945,17 @@
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         boolean result = false;
 
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_DPAD_CENTER:
-            case KeyEvent.KEYCODE_ENTER: {
-                if ((mViewFlags & ENABLED_MASK) == DISABLED) {
-                    return true;
-                }
-                // Long clickable items don't necessarily have to be clickable
-                if (((mViewFlags & CLICKABLE) == CLICKABLE ||
-                        (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) &&
-                        (event.getRepeatCount() == 0)) {
-                    setPressed(true);
-                    checkForLongClick(0);
-                    return true;
-                }
-                break;
+        if (KeyEvent.isConfirmKey(event.getKeyCode())) {
+            if ((mViewFlags & ENABLED_MASK) == DISABLED) {
+                return true;
+            }
+            // Long clickable items don't necessarily have to be clickable
+            if (((mViewFlags & CLICKABLE) == CLICKABLE ||
+                    (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) &&
+                    (event.getRepeatCount() == 0)) {
+                setPressed(true);
+                checkForLongClick(0);
+                return true;
             }
         }
         return result;
@@ -11800,7 +11809,7 @@
             mPrivateFlags &= ~PFLAG_AWAKEN_SCROLL_BARS_ON_ATTACH;
         }
 
-        mPrivateFlags3 &= ~PFLAG3_HAS_LAYOUT;
+        mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
 
         jumpDrawablesToCurrentState();
 
@@ -11944,10 +11953,15 @@
                     if (!canResolveLayoutDirection()) return false;
 
                     // Parent has not yet resolved, LTR is still the default
-                    if (!mParent.isLayoutDirectionResolved()) return false;
+                    try {
+                        if (!mParent.isLayoutDirectionResolved()) return false;
 
-                    if (mParent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
-                        mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL;
+                        if (mParent.getLayoutDirection() == LAYOUT_DIRECTION_RTL) {
+                            mPrivateFlags2 |= PFLAG2_LAYOUT_DIRECTION_RESOLVED_RTL;
+                        }
+                    } catch (AbstractMethodError e) {
+                        Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                                " does not fully implement ViewParent", e);
                     }
                     break;
                 case LAYOUT_DIRECTION_RTL:
@@ -11973,13 +11987,20 @@
      * Check if layout direction resolution can be done.
      *
      * @return true if layout direction resolution can be done otherwise return false.
-     *
-     * @hide
      */
     public boolean canResolveLayoutDirection() {
         switch (getRawLayoutDirection()) {
             case LAYOUT_DIRECTION_INHERIT:
-                return (mParent != null) && mParent.canResolveLayoutDirection();
+                if (mParent != null) {
+                    try {
+                        return mParent.canResolveLayoutDirection();
+                    } catch (AbstractMethodError e) {
+                        Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                                " does not fully implement ViewParent", e);
+                    }
+                }
+                return false;
+
             default:
                 return true;
         }
@@ -12007,7 +12028,6 @@
 
     /**
      * @return true if layout direction has been resolved.
-     * @hide
      */
     public boolean isLayoutDirectionResolved() {
         return (mPrivateFlags2 & PFLAG2_LAYOUT_DIRECTION_RESOLVED) == PFLAG2_LAYOUT_DIRECTION_RESOLVED;
@@ -12088,7 +12108,7 @@
      */
     protected void onDetachedFromWindow() {
         mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
-        mPrivateFlags3 &= ~PFLAG3_HAS_LAYOUT;
+        mPrivateFlags3 &= ~PFLAG3_IS_LAID_OUT;
 
         removeUnsetPressCallback();
         removeLongPressCallback();
@@ -14386,12 +14406,19 @@
      */
     @SuppressWarnings({"unchecked"})
     public void layout(int l, int t, int r, int b) {
+        if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
+            onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
+            mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
+        }
+
         int oldL = mLeft;
         int oldT = mTop;
         int oldB = mBottom;
         int oldR = mRight;
+
         boolean changed = isLayoutModeOptical(mParent) ?
                 setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
+
         if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
             onLayout(changed, l, t, r, b);
             mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
@@ -14406,8 +14433,9 @@
                 }
             }
         }
+
         mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
-        mPrivateFlags3 |= PFLAG3_HAS_LAYOUT;
+        mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
     }
 
     /**
@@ -15883,6 +15911,8 @@
      * handle possible request-during-layout errors correctly.</p>
      */
     public void requestLayout() {
+        if (mMeasureCache != null) mMeasureCache.clear();
+
         if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
             // Only trigger request-during-layout logic if this is the view requesting it,
             // not the views in its parent hierarchy
@@ -15912,6 +15942,8 @@
      * on the parent.
      */
     public void forceLayout() {
+        if (mMeasureCache != null) mMeasureCache.clear();
+
         mPrivateFlags |= PFLAG_FORCE_LAYOUT;
         mPrivateFlags |= PFLAG_INVALIDATED;
     }
@@ -15945,6 +15977,11 @@
             widthMeasureSpec  = MeasureSpec.adjust(widthMeasureSpec,  optical ? -oWidth  : oWidth);
             heightMeasureSpec = MeasureSpec.adjust(heightMeasureSpec, optical ? -oHeight : oHeight);
         }
+
+        // Suppress sign extension for the low bytes
+        long key = (long) widthMeasureSpec << 32 | (long) heightMeasureSpec & 0xffffffffL;
+        if (mMeasureCache == null) mMeasureCache = new LongSparseLongArray(2);
+
         if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
                 widthMeasureSpec != mOldWidthMeasureSpec ||
                 heightMeasureSpec != mOldHeightMeasureSpec) {
@@ -15954,8 +15991,17 @@
 
             resolveRtlPropertiesIfNeeded();
 
-            // measure ourselves, this should set the measured dimension flag back
-            onMeasure(widthMeasureSpec, heightMeasureSpec);
+            int cacheIndex = mMeasureCache.indexOfKey(key);
+            if (cacheIndex < 0) {
+                // measure ourselves, this should set the measured dimension flag back
+                onMeasure(widthMeasureSpec, heightMeasureSpec);
+                mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
+            } else {
+                long value = mMeasureCache.valueAt(cacheIndex);
+                // Casting a long to int drops the high 32 bits, no mask needed
+                setMeasuredDimension((int) (value >> 32), (int) value);
+                mPrivateFlags3 |= PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
+            }
 
             // flag not set, setMeasuredDimension() was not invoked, we raise
             // an exception to warn the developer
@@ -15970,6 +16016,9 @@
 
         mOldWidthMeasureSpec = widthMeasureSpec;
         mOldHeightMeasureSpec = heightMeasureSpec;
+
+        mMeasureCache.put(key, ((long) mMeasuredWidth) << 32 |
+                (long) mMeasuredHeight & 0xffffffffL); // suppress sign extension
     }
 
     /**
@@ -16496,7 +16545,7 @@
     /**
      * Override to find out when the window's requested system UI visibility
      * has changed, that is the value returned by {@link #getWindowSystemUiVisibility()}.
-     * This is different from the callbacks recieved through
+     * This is different from the callbacks received through
      * {@link #setOnSystemUiVisibilityChangeListener(OnSystemUiVisibilityChangeListener)}
      * in that this is only telling you about the local request of the window,
      * not the actual values applied by the system.
@@ -17181,14 +17230,29 @@
                     }
 
                     // Parent has not yet resolved, so we still return the default
-                    if (!mParent.isTextDirectionResolved()) {
-                        mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT;
-                        // Resolution will need to happen again later
-                        return false;
+                    try {
+                        if (!mParent.isTextDirectionResolved()) {
+                            mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT;
+                            // Resolution will need to happen again later
+                            return false;
+                        }
+                    } catch (AbstractMethodError e) {
+                        Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                                " does not fully implement ViewParent", e);
+                        mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_RESOLVED |
+                                PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT;
+                        return true;
                     }
 
                     // Set current resolved direction to the same value as the parent's one
-                    final int parentResolvedDirection = mParent.getTextDirection();
+                    int parentResolvedDirection;
+                    try {
+                        parentResolvedDirection = mParent.getTextDirection();
+                    } catch (AbstractMethodError e) {
+                        Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                                " does not fully implement ViewParent", e);
+                        parentResolvedDirection = TEXT_DIRECTION_LTR;
+                    }
                     switch (parentResolvedDirection) {
                         case TEXT_DIRECTION_FIRST_STRONG:
                         case TEXT_DIRECTION_ANY_RTL:
@@ -17229,13 +17293,20 @@
      * Check if text direction resolution can be done.
      *
      * @return true if text direction resolution can be done otherwise return false.
-     *
-     * @hide
      */
     public boolean canResolveTextDirection() {
         switch (getRawTextDirection()) {
             case TEXT_DIRECTION_INHERIT:
-                return (mParent != null) && mParent.canResolveTextDirection();
+                if (mParent != null) {
+                    try {
+                        return mParent.canResolveTextDirection();
+                    } catch (AbstractMethodError e) {
+                        Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                                " does not fully implement ViewParent", e);
+                    }
+                }
+                return false;
+
             default:
                 return true;
         }
@@ -17265,8 +17336,6 @@
 
     /**
      * @return true if text direction is resolved.
-     *
-     * @hide
      */
     public boolean isTextDirectionResolved() {
         return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED) == PFLAG2_TEXT_DIRECTION_RESOLVED;
@@ -17393,13 +17462,28 @@
                     }
 
                     // Parent has not yet resolved, so we still return the default
-                    if (!mParent.isTextAlignmentResolved()) {
-                        mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT;
-                        // Resolution will need to happen again later
-                        return false;
+                    try {
+                        if (!mParent.isTextAlignmentResolved()) {
+                            mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+                            // Resolution will need to happen again later
+                            return false;
+                        }
+                    } catch (AbstractMethodError e) {
+                        Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                                " does not fully implement ViewParent", e);
+                        mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_RESOLVED |
+                                PFLAG2_TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+                        return true;
                     }
 
-                    final int parentResolvedTextAlignment = mParent.getTextAlignment();
+                    int parentResolvedTextAlignment;
+                    try {
+                        parentResolvedTextAlignment = mParent.getTextAlignment();
+                    } catch (AbstractMethodError e) {
+                        Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                                " does not fully implement ViewParent", e);
+                        parentResolvedTextAlignment = TEXT_ALIGNMENT_GRAVITY;
+                    }
                     switch (parentResolvedTextAlignment) {
                         case TEXT_ALIGNMENT_GRAVITY:
                         case TEXT_ALIGNMENT_TEXT_START:
@@ -17444,13 +17528,20 @@
      * Check if text alignment resolution can be done.
      *
      * @return true if text alignment resolution can be done otherwise return false.
-     *
-     * @hide
      */
     public boolean canResolveTextAlignment() {
         switch (getRawTextAlignment()) {
             case TEXT_DIRECTION_INHERIT:
-                return (mParent != null) && mParent.canResolveTextAlignment();
+                if (mParent != null) {
+                    try {
+                        return mParent.canResolveTextAlignment();
+                    } catch (AbstractMethodError e) {
+                        Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                                " does not fully implement ViewParent", e);
+                    }
+                }
+                return false;
+
             default:
                 return true;
         }
@@ -17480,8 +17571,6 @@
 
     /**
      * @return true if text alignment is resolved.
-     *
-     * @hide
      */
     public boolean isTextAlignmentResolved() {
         return (mPrivateFlags2 & PFLAG2_TEXT_ALIGNMENT_RESOLVED) == PFLAG2_TEXT_ALIGNMENT_RESOLVED;
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a40582b..f574efa 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -742,8 +742,6 @@
 
     /**
      * Called when a child view has changed whether or not it is tracking transient state.
-     *
-     * @hide
      */
     public void childHasTransientStateChanged(View child, boolean childHasTransientState) {
         final boolean oldHasTransientState = hasTransientState();
@@ -764,9 +762,6 @@
         }
     }
 
-    /**
-     * @hide
-     */
     @Override
     public boolean hasTransientState() {
         return mChildCountWithTransientState > 0 || super.hasTransientState();
@@ -2530,13 +2525,15 @@
         event.setClassName(ViewGroup.class.getName());
     }
 
-    /**
-     * @hide
-     */
     @Override
     public void childAccessibilityStateChanged(View root) {
         if (mParent != null) {
-            mParent.childAccessibilityStateChanged(root);
+            try {
+                mParent.childAccessibilityStateChanged(root);
+            } catch (AbstractMethodError e) {
+                Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() +
+                        " does not fully implement ViewParent", e);
+            }
         }
     }
 
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 2ebc1a19..8ae6996 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -271,8 +271,6 @@
      *
      * @param child Child view whose state has changed
      * @param hasTransientState true if this child has transient state
-     *
-     * @hide
      */
     public void childHasTransientStateChanged(View child, boolean hasTransientState);
 
@@ -295,9 +293,7 @@
      * A child notifies its parent that the accessibility state of a subtree rooted
      * at a given node changed. That is the structure of the subtree is different.
      *
-     * @param The root of the changed subtree.
-     *
-     * @hide
+     * @param root The root of the changed subtree.
      */
     public void childAccessibilityStateChanged(View root);
 
@@ -306,8 +302,6 @@
      * See {@link View#setLayoutDirection(int)}
      *
      * @return True if this view parent can resolve the layout direction.
-     *
-     * @hide
      */
     public boolean canResolveLayoutDirection();
 
@@ -316,8 +310,6 @@
      * See {@link View#setLayoutDirection(int)}
      *
      * @return True if this view parent layout direction is resolved.
-     *
-     * @hide
      */
     public boolean isLayoutDirectionResolved();
 
@@ -326,8 +318,6 @@
      *
      * @return {@link View#LAYOUT_DIRECTION_RTL} if the layout direction is RTL or returns
      * {@link View#LAYOUT_DIRECTION_LTR} if the layout direction is not RTL.
-     *
-     * @hide
      */
     public int getLayoutDirection();
 
@@ -336,8 +326,6 @@
      * See {@link View#setTextDirection(int)}
      *
      * @return True if this view parent can resolve the text direction.
-     *
-     * @hide
      */
     public boolean canResolveTextDirection();
 
@@ -346,8 +334,6 @@
      * See {@link View#setTextDirection(int)}
      *
      * @return True if this view parent text direction is resolved.
-     *
-     * @hide
      */
     public boolean isTextDirectionResolved();
 
@@ -361,8 +347,6 @@
      * {@link View#TEXT_DIRECTION_LTR},
      * {@link View#TEXT_DIRECTION_RTL},
      * {@link View#TEXT_DIRECTION_LOCALE}
-     *
-     * @hide
      */
     public int getTextDirection();
 
@@ -371,8 +355,6 @@
      * See {@link View#setTextAlignment(int)}
      *
      * @return True if this view parent can resolve the text alignment.
-     *
-     * @hide
      */
     public boolean canResolveTextAlignment();
 
@@ -381,8 +363,6 @@
      * See {@link View#setTextAlignment(int)}
      *
      * @return True if this view parent text alignment is resolved.
-     *
-     * @hide
      */
     public boolean isTextAlignmentResolved();
 
@@ -397,8 +377,6 @@
      * {@link View#TEXT_ALIGNMENT_TEXT_END},
      * {@link View#TEXT_ALIGNMENT_VIEW_START},
      * {@link View#TEXT_ALIGNMENT_VIEW_END}
-     *
-     * @hide
      */
     public int getTextAlignment();
 }
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 93c6d6e..075719c 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -228,6 +228,7 @@
 
     InputStage mFirstInputStage;
     InputStage mFirstPostImeInputStage;
+    SyntheticInputStage mSyntheticInputStage;
 
     boolean mWindowAttributesChanged = false;
     int mWindowAttributesChangesFlag = 0;
@@ -589,8 +590,8 @@
 
                 // Set up the input pipeline.
                 CharSequence counterSuffix = attrs.getTitle();
-                InputStage syntheticStage = new SyntheticInputStage();
-                InputStage viewPostImeStage = new ViewPostImeInputStage(syntheticStage);
+                mSyntheticInputStage = new SyntheticInputStage();
+                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                 InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                         "aq:native-post-ime:" + counterSuffix);
                 InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
@@ -3773,6 +3774,9 @@
         private int processKeyEvent(QueuedInputEvent q) {
             final KeyEvent event = (KeyEvent)q.mEvent;
 
+            // The synthetic stage occasionally needs to know about keys in order to debounce taps
+            mSyntheticInputStage.notifyKeyEvent(event);
+
             // Deliver the key to the view hierarchy.
             if (mView.dispatchKeyEvent(event)) {
                 return FINISH_HANDLED;
@@ -3945,6 +3949,10 @@
             }
             super.onDeliverToNext(q);
         }
+
+        public void notifyKeyEvent(KeyEvent e) {
+            mTouchNavigation.notifyKeyEvent(e);
+        }
     }
 
     /**
@@ -4375,6 +4383,9 @@
         // Tap timeout in milliseconds.
         private static final int TAP_TIMEOUT = 250;
 
+        // Debounce timeout for touch nav devices with a button under their pad, in milliseconds
+        private static final int DEBOUNCE_TIME = 250;
+
         // The maximum distance traveled for a gesture to be considered a tap in millimeters.
         private static final int TAP_SLOP_MILLIMETERS = 5;
 
@@ -4409,6 +4420,9 @@
         private int mConfigTapTimeout;
         private float mConfigTapSlop;
 
+        // Amount of time to wait between button presses and tap generation for debouncing
+        private int mConfigDebounceTime;
+
         // The scaled tick distance.  A movement of this amount should generally translate
         // into a single dpad event in a given direction.
         private float mConfigTickDistance;
@@ -4454,6 +4468,11 @@
         private boolean mFlinging;
         private float mFlingVelocity;
 
+        // The last time a confirm key was pressed on the touch nav device
+        private long mLastConfirmKeyTime = Long.MAX_VALUE;
+
+        private boolean mHasButtonUnderPad;
+
         public SyntheticTouchNavigationHandler() {
             super(true);
         }
@@ -4497,6 +4516,8 @@
                                 MIN_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
                         mConfigMaxFlingVelocity =
                                 MAX_FLING_VELOCITY_TICKS_PER_SECOND * mConfigTickDistance;
+                        mConfigDebounceTime = DEBOUNCE_TIME;
+                        mHasButtonUnderPad = device.hasButtonUnderPad();
 
                         if (LOCAL_DEBUG) {
                             Log.d(LOCAL_TAG, "Configured device " + mCurrentDeviceId
@@ -4567,10 +4588,13 @@
                         if (!mConsumedMovement
                                 && Math.hypot(mLastX - mStartX, mLastY - mStartY) < mConfigTapSlop
                                 && time <= mStartTime + mConfigTapTimeout) {
-                            // It's a tap!
-                            finishKeys(time);
-                            sendKeyDownOrRepeat(time, KeyEvent.KEYCODE_DPAD_CENTER, metaState);
-                            sendKeyUp(time);
+                            if (!mHasButtonUnderPad ||
+                                        time >= mLastConfirmKeyTime + mConfigDebounceTime) {
+                                // It's a tap!
+                                finishKeys(time);
+                                sendKeyDownOrRepeat(time, KeyEvent.KEYCODE_DPAD_CENTER, metaState);
+                                sendKeyUp(time);
+                            }
                         } else if (mConsumedMovement
                                 && mPendingKeyCode != KeyEvent.KEYCODE_UNKNOWN) {
                             // It might be a fling.
@@ -4603,6 +4627,13 @@
             }
         }
 
+        public void notifyKeyEvent(KeyEvent e) {
+            final int keyCode = e.getKeyCode();
+            if (KeyEvent.isConfirmKey(e.getKeyCode())) {
+                mLastConfirmKeyTime  = e.getDownTime();
+            }
+        }
+
         private void finishKeys(long time) {
             cancelFling();
             sendKeyUp(time);
@@ -6435,11 +6466,17 @@
         public long mLastEventTimeMillis;
 
         public void run() {
-            mLastEventTimeMillis = SystemClock.uptimeMillis();
-            AccessibilityEvent event = AccessibilityEvent.obtain();
-            event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
-            event.setContentChangeType(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
-            mSource.sendAccessibilityEventUnchecked(event);
+            // The accessibility may be turned off while we were waiting so check again.
+            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+                mLastEventTimeMillis = SystemClock.uptimeMillis();
+                AccessibilityEvent event = AccessibilityEvent.obtain();
+                event.setEventType(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+                event.setContentChangeType(AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE);
+                mSource.sendAccessibilityEventUnchecked(event);
+            } else {
+                mLastEventTimeMillis = 0;
+            }
+            // In any case reset to initial state.
             mSource.resetSubtreeAccessibilityStateChanged();
             mSource = null;
         }
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
index 9c00b7f..f0e6677 100644
--- a/core/java/android/view/VolumePanel.java
+++ b/core/java/android/view/VolumePanel.java
@@ -32,6 +32,7 @@
 import android.media.AudioSystem;
 import android.media.RingtoneManager;
 import android.media.ToneGenerator;
+import android.media.VolumeController;
 import android.net.Uri;
 import android.os.Handler;
 import android.os.Message;
@@ -55,7 +56,8 @@
  *
  * @hide
  */
-public class VolumePanel extends Handler implements OnSeekBarChangeListener, View.OnClickListener
+public class VolumePanel extends Handler implements OnSeekBarChangeListener, View.OnClickListener,
+        VolumeController
 {
     private static final String TAG = "VolumePanel";
     private static boolean LOGD = false;
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 5144889..83a58be 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -219,7 +219,8 @@
             @ViewDebug.IntToString(from = TYPE_DREAM, to = "TYPE_DREAM"),
             @ViewDebug.IntToString(from = TYPE_NAVIGATION_BAR_PANEL, to = "TYPE_NAVIGATION_BAR_PANEL"),
             @ViewDebug.IntToString(from = TYPE_DISPLAY_OVERLAY, to = "TYPE_DISPLAY_OVERLAY"),
-            @ViewDebug.IntToString(from = TYPE_MAGNIFICATION_OVERLAY, to = "TYPE_MAGNIFICATION_OVERLAY")
+            @ViewDebug.IntToString(from = TYPE_MAGNIFICATION_OVERLAY, to = "TYPE_MAGNIFICATION_OVERLAY"),
+            @ViewDebug.IntToString(from = TYPE_PRIVATE_PRESENTATION, to = "TYPE_PRIVATE_PRESENTATION")
         })
         public int type;
     
@@ -536,6 +537,12 @@
         public static final int TYPE_KEYGUARD_SCRIM           = FIRST_SYSTEM_WINDOW+29;
 
         /**
+         * Window type: Window for Presentation on top of private
+         * virtual display.
+         */
+        public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;
+
+        /**
          * End of types of system windows.
          */
         public static final int LAST_SYSTEM_WINDOW      = 2999;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
index dded74c..1fde2fa 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfoCache.java
@@ -39,7 +39,7 @@
 
     private static final boolean ENABLED = true;
 
-    private static final boolean DEBUG = true;
+    private static final boolean DEBUG = false;
 
     private static final boolean CHECK_INTEGRITY_IF_DEBUGGABLE_BUILD = true;
 
@@ -108,7 +108,7 @@
 
     private void refreshCachedNode(long sourceId) {
         if (DEBUG) {
-            Log.i(LOG_TAG, "Refresing cached node.");
+            Log.i(LOG_TAG, "Refreshing cached node.");
         }
         synchronized (mLock) {
             AccessibilityNodeInfo cachedInfo = mCacheImpl.get(sourceId);
@@ -117,7 +117,7 @@
                 return;
             }
             // The node changed so we will just refresh it right now.
-            if (cachedInfo.refresh(false)) {
+            if (cachedInfo.refresh(true)) {
                 return;
             }
             // Weird, we could not refresh. Just evict the entire sub-tree.
@@ -141,7 +141,7 @@
                     info = AccessibilityNodeInfo.obtain(info);
                 }
                 if (DEBUG) {
-//                    Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
+                    Log.i(LOG_TAG, "get(" + accessibilityNodeId + ") = " + info);
                 }
                 return info;
             }
@@ -159,7 +159,7 @@
         if (ENABLED) {
             synchronized(mLock) {
                 if (DEBUG) {
-//                    Log.i(LOG_TAG, "add(" + info + ")");
+                    Log.i(LOG_TAG, "add(" + info + ")");
                 }
 
                 final long sourceId = info.getSourceNodeId();
@@ -319,8 +319,6 @@
                         Log.e(LOG_TAG, "Node from: " + info.getWindowId() + " not from:"
                                 + windowId + " " + info);
                     }
-                    mCacheImpl.removeAt(i);
-                    i--;
                 }
             }
         }
diff --git a/core/java/android/view/transition/Crossfade.java b/core/java/android/view/transition/Crossfade.java
index 7cfba70..18bb57f 100644
--- a/core/java/android/view/transition/Crossfade.java
+++ b/core/java/android/view/transition/Crossfade.java
@@ -24,6 +24,7 @@
 import android.animation.ValueAnimator;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.Rect;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
@@ -79,6 +80,15 @@
      * @see #setFadeBehavior(int)
      */
     public static final int FADE_BEHAVIOR_REVEAL = 1;
+    /**
+     * Flag specifying that the fading animation should first fade
+     * out the original representation completely and then fade in the
+     * new one. This effect may be more suitable than the other
+     * fade behaviors for views with.
+     *
+     * @see #setFadeBehavior(int)
+     */
+    public static final int FADE_BEHAVIOR_OUT_IN = 2;
 
     /**
      * Flag specifying that the transition should not animate any
@@ -112,7 +122,7 @@
      * transition is run.
      */
     public void setFadeBehavior(int fadeBehavior) {
-        if (fadeBehavior >= FADE_BEHAVIOR_CROSSFADE && fadeBehavior <= FADE_BEHAVIOR_REVEAL) {
+        if (fadeBehavior >= FADE_BEHAVIOR_CROSSFADE && fadeBehavior <= FADE_BEHAVIOR_OUT_IN) {
             mFadeBehavior = fadeBehavior;
         }
     }
@@ -145,6 +155,7 @@
         if (startValues == null || endValues == null) {
             return null;
         }
+        final boolean useParentOverlay = mFadeBehavior != FADE_BEHAVIOR_REVEAL;
         final View view = endValues.view;
         Map<String, Object> startVals = startValues.values;
         Map<String, Object> endVals = endValues.values;
@@ -159,8 +170,8 @@
                     " for start, end: " + startBitmap + ", " + endBitmap);
         }
         if (startDrawable != null && endDrawable != null && !startBitmap.sameAs(endBitmap)) {
-            ViewOverlay overlay = (mFadeBehavior == FADE_BEHAVIOR_REVEAL) ?
-                    view.getOverlay() : ((ViewGroup) view.getParent()).getOverlay();
+            ViewOverlay overlay = useParentOverlay ?
+                    ((ViewGroup) view.getParent()).getOverlay() : view.getOverlay();
             if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
                 overlay.add(endDrawable);
             }
@@ -169,7 +180,13 @@
             // gradually fading out the start drawable. So it's not really a cross-fade, but rather
             // a reveal of the end scene over time. Also, animate the bounds of both drawables
             // to mimic the change in the size of the view itself between scenes.
-            ObjectAnimator anim = ObjectAnimator.ofInt(startDrawable, "alpha", 0);
+            ObjectAnimator anim;
+            if (mFadeBehavior == FADE_BEHAVIOR_OUT_IN) {
+                // Fade out completely halfway through the transition
+                anim = ObjectAnimator.ofInt(startDrawable, "alpha", 255, 0, 0);
+            } else {
+                anim = ObjectAnimator.ofInt(startDrawable, "alpha", 0);
+            }
             anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                 @Override
                 public void onAnimationUpdate(ValueAnimator animation) {
@@ -178,7 +195,10 @@
                 }
             });
             ObjectAnimator anim1 = null;
-            if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
+            if (mFadeBehavior == FADE_BEHAVIOR_OUT_IN) {
+                // start fading in halfway through the transition
+                anim1 = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 0, 1);
+            } else if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
                 anim1 = ObjectAnimator.ofFloat(view, View.ALPHA, 0, 1);
             }
             if (Transition.DBG) {
@@ -188,8 +208,8 @@
             anim.addListener(new AnimatorListenerAdapter() {
                 @Override
                 public void onAnimationEnd(Animator animation) {
-                    ViewOverlay overlay = (mFadeBehavior == FADE_BEHAVIOR_REVEAL) ?
-                            view.getOverlay() : ((ViewGroup) view.getParent()).getOverlay();
+                    ViewOverlay overlay = useParentOverlay ?
+                            ((ViewGroup) view.getParent()).getOverlay() : view.getOverlay();
                     overlay.remove(startDrawable);
                     if (mFadeBehavior == FADE_BEHAVIOR_REVEAL) {
                         overlay.remove(endDrawable);
@@ -227,7 +247,7 @@
     protected void captureValues(TransitionValues values, boolean start) {
         View view = values.view;
         Rect bounds = new Rect(0, 0, view.getWidth(), view.getHeight());
-        if (mFadeBehavior == FADE_BEHAVIOR_CROSSFADE) {
+        if (mFadeBehavior != FADE_BEHAVIOR_REVEAL) {
             bounds.offset(view.getLeft(), view.getTop());
         }
         values.values.put(PROPNAME_BOUNDS, bounds);
diff --git a/core/java/android/view/transition/Fade.java b/core/java/android/view/transition/Fade.java
index 3c5b6fa..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";
@@ -96,12 +98,19 @@
     protected Animator appear(ViewGroup sceneRoot,
             TransitionValues startValues, int startVisibility,
             TransitionValues endValues, int endVisibility) {
-        View endView = (endValues != null) ? endValues.view : null;
-        if ((mFadingMode & IN) != IN) {
+        if ((mFadingMode & IN) != IN || endValues == null) {
             return null;
         }
+        final View endView = endValues.view;
         endView.setAlpha(0);
-        return runAnimation(endView, 0, 1, null);
+        final Animator.AnimatorListener endListener = new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                // Always end animation with full alpha, in case it's canceled mid-stream
+                endView.setAlpha(1);
+            }
+        };
+        return runAnimation(endView, 0, 1, endListener);
     }
 
     @Override
@@ -114,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/TextChange.java b/core/java/android/view/transition/TextChange.java
index f033e63..04ff707 100644
--- a/core/java/android/view/transition/TextChange.java
+++ b/core/java/android/view/transition/TextChange.java
@@ -18,7 +18,9 @@
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
 import android.animation.ValueAnimator;
+import android.graphics.Color;
 import android.util.ArrayMap;
 import android.view.ViewGroup;
 import android.widget.TextView;
@@ -34,15 +36,69 @@
  */
 public class TextChange extends Transition {
     private static final String PROPNAME_TEXT = "android:textchange:text";
+    private static final String PROPNAME_TEXT_COLOR = "android:textchange:textColor";
 
-    // TODO: think about other options we could have here, like cross-fading the text, or fading
-    // it out/in. These could be parameters to supply to the constructors (and xml attributes).
+    private int mChangeBehavior = CHANGE_BEHAVIOR_KEEP;
+
+    /**
+     * Flag specifying that the text in affected/changing TextView targets will keep
+     * their original text during the transition, setting it to the final text when
+     * the transition ends. This is the default behavior.
+     *
+     * @see #setChangeBehavior(int)
+     */
+    public static final int CHANGE_BEHAVIOR_KEEP = 0;
+    /**
+     * Flag specifying that the text changing animation should first fade
+     * out the original text completely. The new text is set on the target
+     * view at the end of the fade-out animation. This transition is typically
+     * used with a later {@link #CHANGE_BEHAVIOR_IN} transition, allowing more
+     * flexibility than the {@link #CHANGE_BEHAVIOR_OUT_IN} by allowing other
+     * transitions to be run sequentially or in parallel with these fades.
+     *
+     * @see #setChangeBehavior(int)
+     */
+    public static final int CHANGE_BEHAVIOR_OUT = 1;
+    /**
+     * Flag specifying that the text changing animation should fade in the
+     * end text into the affected target view(s). This transition is typically
+     * used in conjunction with an earlier {@link #CHANGE_BEHAVIOR_OUT}
+     * transition, possibly with other transitions running as well, such as
+     * a sequence to fade out, then resize the view, then fade in.
+     *
+     * @see #setChangeBehavior(int)
+     */
+    public static final int CHANGE_BEHAVIOR_IN = 2;
+    /**
+     * Flag specifying that the text changing animation should first fade
+     * out the original text completely and then fade in the
+     * new text.
+     *
+     * @see #setChangeBehavior(int)
+     */
+    public static final int CHANGE_BEHAVIOR_OUT_IN = 3;
+
+    /**
+     * Sets the type of changing animation that will be run, one of
+     * {@link #CHANGE_BEHAVIOR_KEEP} and {@link #CHANGE_BEHAVIOR_OUT_IN}.
+     *
+     * @param changeBehavior The type of fading animation to use when this
+     * transition is run.
+     */
+    public void setChangeBehavior(int changeBehavior) {
+        if (changeBehavior >= CHANGE_BEHAVIOR_KEEP && changeBehavior <= CHANGE_BEHAVIOR_OUT_IN) {
+            mChangeBehavior = changeBehavior;
+        }
+    }
 
     @Override
     protected void captureValues(TransitionValues values, boolean start) {
         if (values.view instanceof TextView) {
             TextView textview = (TextView) values.view;
             values.values.put(PROPNAME_TEXT, textview.getText());
+            if (mChangeBehavior > CHANGE_BEHAVIOR_KEEP) {
+                values.values.put(PROPNAME_TEXT_COLOR, textview.getCurrentTextColor());
+            }
         }
     }
 
@@ -59,13 +115,60 @@
         final String endText = (String) endVals.get(PROPNAME_TEXT);
         if (!startText.equals(endText)) {
             view.setText(startText);
-            ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
-            anim.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    view.setText(endText);
+            Animator anim;
+            if (mChangeBehavior == CHANGE_BEHAVIOR_KEEP) {
+                anim = ValueAnimator.ofFloat(0, 1);
+                anim.addListener(new AnimatorListenerAdapter() {
+                    @Override
+                    public void onAnimationEnd(Animator animation) {
+                        view.setText(endText);
+                    }
+                });
+            } else {
+                // Fade out start text
+                final int startColor = (Integer) startVals.get(PROPNAME_TEXT_COLOR);
+                final int endColor = (Integer) endVals.get(PROPNAME_TEXT_COLOR);
+                ValueAnimator outAnim = null, inAnim = null;
+                if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN ||
+                        mChangeBehavior == CHANGE_BEHAVIOR_OUT) {
+                    outAnim = ValueAnimator.ofInt(255, 0);
+                    outAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                        @Override
+                        public void onAnimationUpdate(ValueAnimator animation) {
+                            int currAlpha = (Integer) animation.getAnimatedValue();
+                            view.setTextColor(currAlpha << 24 | Color.red(startColor) << 16 |
+                                    Color.green(startColor) << 8 | Color.red(startColor));
+                        }
+                    });
+                    outAnim.addListener(new AnimatorListenerAdapter() {
+                        @Override
+                        public void onAnimationEnd(Animator animation) {
+                            view.setText(endText);
+                        }
+                    });
                 }
-            });
+                if (mChangeBehavior == CHANGE_BEHAVIOR_OUT_IN ||
+                        mChangeBehavior == CHANGE_BEHAVIOR_IN) {
+                    inAnim = ValueAnimator.ofInt(0, 255);
+                    inAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+                        @Override
+                        public void onAnimationUpdate(ValueAnimator animation) {
+                            int currAlpha = (Integer) animation.getAnimatedValue();
+                            view.setTextColor(currAlpha << 24 | Color.red(endColor) << 16 |
+                                    Color.green(endColor) << 8 | Color.red(endColor));
+                        }
+                    });
+                }
+                if (outAnim != null && inAnim != null) {
+                    anim = new AnimatorSet();
+                    ((AnimatorSet) anim).playSequentially(outAnim, inAnim);
+                } else if (outAnim != null) {
+                    anim = outAnim;
+                } else {
+                    // Must be an in-only animation
+                    anim = inAnim;
+                }
+            }
             return anim;
         }
         return null;
diff --git a/core/java/android/view/transition/Transition.java b/core/java/android/view/transition/Transition.java
index 2cffd26..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();
@@ -509,6 +547,15 @@
      * false otherwise
      */
     void captureValues(ViewGroup sceneRoot, boolean start) {
+        if (start) {
+            mStartValues.viewValues.clear();
+            mStartValues.idValues.clear();
+            mStartValues.itemIdValues.clear();
+        } else {
+            mEndValues.viewValues.clear();
+            mEndValues.idValues.clear();
+            mEndValues.itemIdValues.clear();
+        }
         if (mTargetIds != null && mTargetIds.length > 0 ||
                 mTargets != null && mTargets.length > 0) {
             if (mTargetIds != null) {
@@ -774,13 +821,6 @@
                     v.setHasTransientState(false);
                 }
             }
-            mStartValues.viewValues.clear();
-            mStartValues.idValues.clear();
-            mStartValues.itemIdValues.clear();
-            mEndValues.viewValues.clear();
-            mEndValues.idValues.clear();
-            mEndValues.itemIdValues.clear();
-            mCurrentAnimators.clear();
         }
     }
 
@@ -869,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/TransitionGroup.java b/core/java/android/view/transition/TransitionGroup.java
index d0e61ea..313e33e 100644
--- a/core/java/android/view/transition/TransitionGroup.java
+++ b/core/java/android/view/transition/TransitionGroup.java
@@ -104,7 +104,7 @@
                 mTransitions.add(transitions[i]);
                 transitions[i].mParent = this;
                 if (mDuration >= 0) {
-                    transitions[0].setDuration(mDuration);
+                    transitions[i].setDuration(mDuration);
                 }
             }
         }
diff --git a/core/java/android/view/transition/TransitionManager.java b/core/java/android/view/transition/TransitionManager.java
index 59b07b1..7836268 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() {
@@ -182,16 +186,17 @@
 
     private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) {
 
-        Transition runningTransition = sRunningTransitions.get(sceneRoot);
-        if (runningTransition != null) {
-            runningTransition.cancelTransition();
-        }
-
         // Capture current values
+        Transition runningTransition = sRunningTransitions.get(sceneRoot);
+
         if (transition != null) {
             transition.captureValues(sceneRoot, true);
         }
 
+        if (runningTransition != null) {
+            runningTransition.cancelTransition();
+        }
+
         // Notify previous scene that it is being exited
         Scene previousScene = sceneRoot.getCurrentScene();
         if (previousScene != null) {
@@ -315,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.isLaidOut()) {
+            if (Transition.DBG) {
+                Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " +
+                        sceneRoot + ", " + transition);
+            }
             sPendingTransitions.add(sceneRoot);
             if (transition == null) {
                 transition = sDefaultTransition;
@@ -324,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/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 4998742..7146d0d 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -41,6 +41,7 @@
 import android.view.ViewTreeObserver;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.widget.AbsoluteLayout;
@@ -849,6 +850,23 @@
     }
 
     /**
+     * Asynchronously evaluates JavaScript in the context of the currently displayed page.
+     * If non-null, |resultCallback| will be invoked with any result returned from that
+     * execution. This method must be called on the UI thread and the callback will
+     * be made on the UI thread.
+     *
+     * @param script the JavaScript to execute.
+     * @param resultCallback A callback to be invoked when the script execution
+     *                       completes with the result of the execution (if any).
+     *                       May be null if no notificaion of the result is required.
+     * @hide pending API council approval and CTS test coverage.
+     */
+    public void evaluateJavascript(String script, ValueCallback<String> resultCallback) {
+        checkThread();
+        mProvider.evaluateJavaScript(script, resultCallback);
+    }
+
+    /**
      * Saves the current view as a web archive.
      *
      * @param filename the filename where the archive should be placed
@@ -2051,6 +2069,13 @@
     }
     */
 
+    @Override
+    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+        AccessibilityNodeProvider provider =
+                mProvider.getViewDelegate().getAccessibilityNodeProvider();
+        return provider == null ? super.getAccessibilityNodeProvider() : provider;
+    }
+
     @Deprecated
     @Override
     public boolean shouldDelayChildPressedState() {
diff --git a/core/java/android/webkit/WebViewClassic.java b/core/java/android/webkit/WebViewClassic.java
index 3afab09..b930276 100644
--- a/core/java/android/webkit/WebViewClassic.java
+++ b/core/java/android/webkit/WebViewClassic.java
@@ -90,6 +90,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -1769,6 +1770,11 @@
     }
 
     @Override
+    public AccessibilityNodeProvider getAccessibilityNodeProvider() {
+      return null;
+    }
+
+    @Override
     public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
         if (!mWebView.isEnabled()) {
             // Only default actions are supported while disabled.
@@ -2650,6 +2656,12 @@
         clearHelpers();
     }
 
+    @Override
+    public void evaluateJavaScript(String script, ValueCallback<String> resultCallback) {
+        // K-only API not implemented in WebViewClassic.
+        throw new IllegalStateException("This API not supported in Classic WebView.");
+    }
+
     /**
      * See {@link WebView#saveWebArchive(String)}
      */
diff --git a/core/java/android/webkit/WebViewProvider.java b/core/java/android/webkit/WebViewProvider.java
index fa17ab9..8c5c4ce 100644
--- a/core/java/android/webkit/WebViewProvider.java
+++ b/core/java/android/webkit/WebViewProvider.java
@@ -32,6 +32,7 @@
 import android.view.ViewGroup.LayoutParams;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.AccessibilityNodeProvider;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
 import android.webkit.WebView.HitTestResult;
@@ -113,6 +114,8 @@
     public void loadDataWithBaseURL(String baseUrl, String data,
             String mimeType, String encoding, String historyUrl);
 
+    public void evaluateJavaScript(String script, ValueCallback<String> resultCallback);
+
     public void saveWebArchive(String filename);
 
     public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback);
@@ -279,6 +282,8 @@
     interface ViewDelegate {
         public boolean shouldDelayChildPressedState();
 
+        public AccessibilityNodeProvider getAccessibilityNodeProvider();
+
         public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info);
 
         public void onInitializeAccessibilityEvent(AccessibilityEvent event);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1e20ab2..f5c3b37 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -28,6 +28,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.StrictMode;
+import android.os.Trace;
 import android.text.Editable;
 import android.text.InputType;
 import android.text.TextUtils;
@@ -1206,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);
         }
     }
 
@@ -2151,6 +2150,8 @@
      * @return A view displaying the data associated with the specified position
      */
     View obtainView(int position, boolean[] isScrap) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "obtainView");
+
         isScrap[0] = false;
         View scrapView;
 
@@ -2213,6 +2214,8 @@
             }
         }
 
+        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
+
         return child;
     }
 
@@ -2922,9 +2925,7 @@
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        switch (keyCode) {
-        case KeyEvent.KEYCODE_DPAD_CENTER:
-        case KeyEvent.KEYCODE_ENTER:
+        if (KeyEvent.isConfirmKey(keyCode)) {
             if (!isEnabled()) {
                 return true;
             }
@@ -2940,7 +2941,6 @@
                 setPressed(false);
                 return true;
             }
-            break;
         }
         return super.onKeyUp(keyCode, event);
     }
@@ -6497,58 +6497,67 @@
         }
 
         /**
-         * Put a view into the ScrapViews list. These views are unordered.
+         * Puts a view into the list of scrap views.
+         * <p>
+         * If the list data hasn't changed or the adapter has stable IDs, views
+         * with transient state will be preserved for later retrieval.
          *
          * @param scrap The view to add
+         * @param position The view's position within its parent
          */
         void addScrapView(View scrap, int position) {
-            AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
+            final AbsListView.LayoutParams lp = (AbsListView.LayoutParams) scrap.getLayoutParams();
             if (lp == null) {
                 return;
             }
 
             lp.scrappedFromPosition = position;
 
-            // Don't put header or footer views or views that should be ignored
-            // into the scrap heap
-            int viewType = lp.viewType;
+            // Don't scrap header or footer views, or views that should
+            // otherwise not be recycled.
+            final int viewType = lp.viewType;
+            if (!shouldRecycleViewType(viewType)) {
+                return;
+            }
+
+            scrap.dispatchStartTemporaryDetach();
+
+            // Don't scrap views that have transient state.
             final boolean scrapHasTransientState = scrap.hasTransientState();
-            if (!shouldRecycleViewType(viewType) || scrapHasTransientState) {
-                if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER && scrapHasTransientState) {
+            if (scrapHasTransientState) {
+                if (mAdapter != null && mAdapterHasStableIds) {
+                    // If the adapter has stable IDs, we can reuse the view for
+                    // the same data.
+                    if (mTransientStateViewsById == null) {
+                        mTransientStateViewsById = new LongSparseArray<View>();
+                    }
+                    mTransientStateViewsById.put(lp.itemId, scrap);
+                } else if (!mDataChanged) {
+                    // If the data hasn't changed, we can reuse the views at
+                    // their old positions.
+                    if (mTransientStateViews == null) {
+                        mTransientStateViews = new SparseArray<View>();
+                    }
+                    mTransientStateViews.put(position, scrap);
+                } else {
+                    // Otherwise, we'll have to remove the view and start over.
                     if (mSkippedScrap == null) {
                         mSkippedScrap = new ArrayList<View>();
                     }
                     mSkippedScrap.add(scrap);
                 }
-                if (scrapHasTransientState) {
-                    scrap.dispatchStartTemporaryDetach();
-                    if (mAdapter != null && mAdapterHasStableIds) {
-                        if (mTransientStateViewsById == null) {
-                            mTransientStateViewsById = new LongSparseArray<View>();
-                        }
-                        mTransientStateViewsById.put(lp.itemId, scrap);
-                    } else if (!mDataChanged) {
-                        // avoid putting views on transient state list during a data change;
-                        // the layout positions may be out of sync with the adapter positions
-                        if (mTransientStateViews == null) {
-                            mTransientStateViews = new SparseArray<View>();
-                        }
-                        mTransientStateViews.put(position, scrap);
-                    }
-                }
-                return;
-            }
-
-            scrap.dispatchStartTemporaryDetach();
-            if (mViewTypeCount == 1) {
-                mCurrentScrap.add(scrap);
             } else {
-                mScrapViews[viewType].add(scrap);
-            }
+                if (mViewTypeCount == 1) {
+                    mCurrentScrap.add(scrap);
+                } else {
+                    mScrapViews[viewType].add(scrap);
+                }
 
-            scrap.setAccessibilityDelegate(null);
-            if (mRecyclerListener != null) {
-                mRecyclerListener.onMovedToScrapHeap(scrap);
+                scrap.setAccessibilityDelegate(null);
+
+                if (mRecyclerListener != null) {
+                    mRecyclerListener.onMovedToScrapHeap(scrap);
+                }
             }
         }
 
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index f1c3139..40747f0 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -242,7 +242,7 @@
                 right = width - mBasePadding;
                 left = right - mCheckMarkWidth;
             }
-            checkMarkDrawable.setBounds( left, top, right, bottom);
+            checkMarkDrawable.setBounds(mScrollX + left, top, mScrollX + right, bottom);
             checkMarkDrawable.draw(canvas);
         }
     }
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index c4406ac..05a8dc8 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -199,10 +199,8 @@
                 unscheduleDrawable(mButtonDrawable);
             }
             d.setCallback(this);
-            d.setState(getDrawableState());
             d.setVisible(getVisibility() == VISIBLE, false);
             mButtonDrawable = d;
-            mButtonDrawable.setState(null);
             setMinHeight(mButtonDrawable.getIntrinsicHeight());
         }
 
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/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index c4ef11c..78ba6e0 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -1228,13 +1228,9 @@
 
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
-        switch (keyCode) {
-        case KeyEvent.KEYCODE_DPAD_CENTER:
-        case KeyEvent.KEYCODE_ENTER: {
-            
+        if (KeyEvent.isConfirmKey(keyCode)) {
             if (mReceivedInvokeKeyDown) {
                 if (mItemCount > 0) {
-    
                     dispatchPress(mSelectedChild);
                     postDelayed(new Runnable() {
                         @Override
@@ -1242,20 +1238,17 @@
                             dispatchUnpress();
                         }
                     }, ViewConfiguration.getPressedStateDuration());
-    
+
                     int selectedIndex = mSelectedPosition - mFirstPosition;
                     performItemClick(getChildAt(selectedIndex), mSelectedPosition, mAdapter
                             .getItemId(mSelectedPosition));
                 }
             }
-            
+
             // Clear the flag
             mReceivedInvokeKeyDown = false;
-            
             return true;
         }
-        }
-
         return super.onKeyUp(keyCode, event);
     }
     
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 63147dd..a7d546a 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -20,6 +20,7 @@
 import android.content.Intent;
 import android.content.res.TypedArray;
 import android.graphics.Rect;
+import android.os.Trace;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.KeyEvent;
@@ -1364,6 +1365,8 @@
      */
     private void setupChild(View child, int position, int y, boolean flow, int childrenLeft,
             boolean selected, boolean recycled, int where) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "setupGridItem");
+
         boolean isSelected = selected && shouldShowSelector();
         final boolean updateChildSelected = isSelected != child.isSelected();
         final int mode = mTouchMode;
@@ -1459,6 +1462,8 @@
                 != position) {
             child.jumpDrawablesToCurrentState();
         }
+
+        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
 
     /**
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index d114b76..dab0962 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -1480,7 +1480,7 @@
         }
         mChildToScrollTo = null;
 
-        if (!hasLayout()) {
+        if (!isLaidOut()) {
             final int scrollRange = Math.max(0,
                     childWidth - (r - l - mPaddingLeft - mPaddingRight));
             if (mSavedState != null) {
diff --git a/core/java/android/widget/ListPopupWindow.java b/core/java/android/widget/ListPopupWindow.java
index 3d6b69e..414c318 100644
--- a/core/java/android/widget/ListPopupWindow.java
+++ b/core/java/android/widget/ListPopupWindow.java
@@ -821,8 +821,7 @@
             // to select one of its items
             if (keyCode != KeyEvent.KEYCODE_SPACE
                     && (mDropDownList.getSelectedItemPosition() >= 0
-                            || (keyCode != KeyEvent.KEYCODE_ENTER
-                                    && keyCode != KeyEvent.KEYCODE_DPAD_CENTER))) {
+                            || !KeyEvent.isConfirmKey(keyCode))) {
                 int curIndex = mDropDownList.getSelectedItemPosition();
                 boolean consumed;
 
@@ -910,16 +909,10 @@
     public boolean onKeyUp(int keyCode, KeyEvent event) {
         if (isShowing() && mDropDownList.getSelectedItemPosition() >= 0) {
             boolean consumed = mDropDownList.onKeyUp(keyCode, event);
-            if (consumed) {
-                switch (keyCode) {
-                    // if the list accepts the key events and the key event
-                    // was a click, the text view gets the selected item
-                    // from the drop down as its content
-                    case KeyEvent.KEYCODE_ENTER:
-                    case KeyEvent.KEYCODE_DPAD_CENTER:
-                        dismiss();
-                        break;
-                }
+            if (consumed && KeyEvent.isConfirmKey(keyCode)) {
+                // if the list accepts the key events and the key event was a click, the text view
+                // gets the selected item from the drop down as its content
+                dismiss();
             }
             return consumed;
         }
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index d990a20..2f42ae3 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -16,6 +16,7 @@
 
 package android.widget;
 
+import android.os.Trace;
 import com.android.internal.R;
 import com.android.internal.util.Predicate;
 import com.google.android.collect.Lists;
@@ -1477,12 +1478,12 @@
     @Override
     protected void layoutChildren() {
         final boolean blockLayoutRequests = mBlockLayoutRequests;
-        if (!blockLayoutRequests) {
-            mBlockLayoutRequests = true;
-        } else {
+        if (blockLayoutRequests) {
             return;
         }
 
+        mBlockLayoutRequests = true;
+
         try {
             super.layoutChildren();
 
@@ -1494,10 +1495,10 @@
                 return;
             }
 
-            int childrenTop = mListPadding.top;
-            int childrenBottom = mBottom - mTop - mListPadding.bottom;
+            final int childrenTop = mListPadding.top;
+            final int childrenBottom = mBottom - mTop - mListPadding.bottom;
+            final int childCount = getChildCount();
 
-            int childCount = getChildCount();
             int index = 0;
             int delta = 0;
 
@@ -1506,8 +1507,6 @@
             View oldFirst = null;
             View newSel = null;
 
-            View focusLayoutRestoreView = null;
-
             AccessibilityNodeInfo accessibilityFocusLayoutRestoreNode = null;
             View accessibilityFocusLayoutRestoreView = null;
             int accessibilityFocusPosition = INVALID_POSITION;
@@ -1569,6 +1568,7 @@
             // Remember which child, if any, had accessibility focus. This must
             // occur before recycling any views, since that will clear
             // accessibility focus.
+            // TODO: This should rely on transient state.
             final ViewRootImpl viewRootImpl = getViewRootImpl();
             if (viewRootImpl != null) {
                 final View accessFocusedView = viewRootImpl.getAccessibilityFocusedHost();
@@ -1592,16 +1592,18 @@
                 }
             }
 
+            // Ensure the child containing focus, if any, has transient state.
+            // If the list data hasn't changed, or if the adapter has stable
+            // IDs, this will maintain focus.
+            final View focusedChild = getFocusedChild();
+            if (focusedChild != null) {
+                focusedChild.setHasTransientState(true);
+            }
+
             // Pull all children into the RecycleBin.
             // These views will be reused if possible
             final int firstPosition = mFirstPosition;
             final RecycleBin recycleBin = mRecycler;
-
-            // reset the focus restoration
-            View focusLayoutRestoreDirectChild = null;
-
-            // Don't put header or footer views into the Recycler. Those are
-            // already cached in mHeaderViews;
             if (dataChanged) {
                 for (int i = 0; i < childCount; i++) {
                     recycleBin.addScrapView(getChildAt(i), firstPosition+i);
@@ -1610,28 +1612,6 @@
                 recycleBin.fillActiveViews(childCount, firstPosition);
             }
 
-            // take focus back to us temporarily to avoid the eventual
-            // call to clear focus when removing the focused child below
-            // from messing things up when ViewAncestor assigns focus back
-            // to someone else
-            final View focusedChild = getFocusedChild();
-            if (focusedChild != null) {
-                // TODO: in some cases focusedChild.getParent() == null
-
-                // we can remember the focused view to restore after relayout if the
-                // data hasn't changed, or if the focused position is a header or footer
-                if (!dataChanged || isDirectChildHeaderOrFooter(focusedChild)) {
-                    focusLayoutRestoreDirectChild = focusedChild;
-                    // remember the specific view that had focus
-                    focusLayoutRestoreView = findFocus();
-                    if (focusLayoutRestoreView != null) {
-                        // tell it we are going to mess with it
-                        focusLayoutRestoreView.onStartTemporaryDetach();
-                    }
-                }
-                requestFocus();
-            }
-
             // Clear out old views
             detachAllViewsFromParent();
             recycleBin.removeSkippedScrap();
@@ -1691,43 +1671,37 @@
             recycleBin.scrapActiveViews();
 
             if (sel != null) {
-                // the current selected item should get focus if items
-                // are focusable
-                if (mItemsCanFocus && hasFocus() && !sel.hasFocus()) {
-                    final boolean focusWasTaken = (sel == focusLayoutRestoreDirectChild &&
-                            focusLayoutRestoreView != null &&
-                            focusLayoutRestoreView.requestFocus()) || sel.requestFocus();
-                    if (!focusWasTaken) {
-                        // selected item didn't take focus, fine, but still want
-                        // to make sure something else outside of the selected view
-                        // has focus
+                final boolean shouldPlaceFocus = mItemsCanFocus && hasFocus();
+                final boolean maintainedFocus = focusedChild != null && focusedChild.hasFocus();
+                if (shouldPlaceFocus && !maintainedFocus && !sel.hasFocus()) {
+                    if (sel.requestFocus()) {
+                        // Successfully placed focus, clear selection.
+                        sel.setSelected(false);
+                        mSelectorRect.setEmpty();
+                    } else {
+                        // Failed to place focus, clear current (invalid) focus.
                         final View focused = getFocusedChild();
                         if (focused != null) {
                             focused.clearFocus();
                         }
                         positionSelector(INVALID_POSITION, sel);
-                    } else {
-                        sel.setSelected(false);
-                        mSelectorRect.setEmpty();
                     }
                 } else {
                     positionSelector(INVALID_POSITION, sel);
                 }
                 mSelectedTop = sel.getTop();
             } else {
-                if (mTouchMode > TOUCH_MODE_DOWN && mTouchMode < TOUCH_MODE_SCROLL) {
-                    View child = getChildAt(mMotionPosition - mFirstPosition);
-                    if (child != null) positionSelector(mMotionPosition, child);
+                // If the user's finger is down, select the motion position.
+                // Otherwise, clear selection.
+                if (mTouchMode == TOUCH_MODE_TAP || mTouchMode == TOUCH_MODE_DONE_WAITING) {
+                    final View child = getChildAt(mMotionPosition - mFirstPosition);
+                    if (child != null)  {
+                        positionSelector(mMotionPosition, child);
+                    }
                 } else {
                     mSelectedTop = 0;
                     mSelectorRect.setEmpty();
                 }
-
-                // even if there is not selected position, we may need to restore
-                // focus (i.e. something focusable in touch mode)
-                if (hasFocus() && focusLayoutRestoreView != null) {
-                    focusLayoutRestoreView.requestFocus();
-                }
             }
 
             // Attempt to restore accessibility focus.
@@ -1752,11 +1726,8 @@
                 }
             }
 
-            // tell focus view we are done mucking with it, if it is still in
-            // our view hierarchy.
-            if (focusLayoutRestoreView != null
-                    && focusLayoutRestoreView.getWindowToken() != null) {
-                focusLayoutRestoreView.onFinishTemporaryDetach();
+            if (focusedChild != null) {
+                focusedChild.setHasTransientState(false);
             }
             
             mLayoutMode = LAYOUT_NORMAL;
@@ -1876,6 +1847,8 @@
      */
     private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft,
             boolean selected, boolean recycled) {
+        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "setupListItem");
+
         final boolean isSelected = selected && shouldShowSelector();
         final boolean updateChildSelected = isSelected != child.isSelected();
         final int mode = mTouchMode;
@@ -1956,6 +1929,8 @@
                 != position) {
             child.jumpDrawablesToCurrentState();
         }
+
+        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
 
     @Override
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 07e66b7..29b4708 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -37,6 +37,7 @@
 import android.os.StrictMode;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.LayoutInflater.Filter;
@@ -136,6 +137,16 @@
 
     private static final OnClickHandler DEFAULT_ON_CLICK_HANDLER = new OnClickHandler();
 
+    private static final Object sMethodsLock = new Object[0];
+    private static final ArrayMap<Class<? extends View>, ArrayMap<String, Method>> sMethods =
+            new ArrayMap<Class<? extends View>, ArrayMap<String, Method>>();
+    private static final ThreadLocal<Object[]> sInvokeArgsTls = new ThreadLocal<Object[]>() {
+        @Override
+        protected Object[] initialValue() {
+            return new Object[1];
+        }
+    };
+
     /**
      * This annotation indicates that a subclass of View is alllowed to be used
      * with the {@link RemoteViews} mechanism.
@@ -206,9 +217,8 @@
          * Overridden by each class to report on it's own memory usage
          */
         public void updateMemoryUsageEstimate(MemoryUsageCounter counter) {
-            // We currently only calculate Bitmap memory usage, so by default, don't do anything
-            // here
-            return;
+            // We currently only calculate Bitmap memory usage, so by default,
+            // don't do anything here
         }
 
         public void setBitmapCache(BitmapCache bitmapCache) {
@@ -346,7 +356,7 @@
             }
             if (target == root) {
                 target.setTagInternal(com.android.internal.R.id.fillInIntent, fillInIntent);
-            } else if (target != null && fillInIntent != null) {
+            } else if (fillInIntent != null) {
                 OnClickListener listener = new OnClickListener() {
                     public void onClick(View v) {
                         // Insure that this view is a child of an AdapterView
@@ -372,16 +382,7 @@
 
                         PendingIntent pendingIntent = (PendingIntent) parent.getTag();
 
-                        final float appScale = v.getContext().getResources()
-                                .getCompatibilityInfo().applicationScale;
-                        final int[] pos = new int[2];
-                        v.getLocationOnScreen(pos);
-
-                        final Rect rect = new Rect();
-                        rect.left = (int) (pos[0] * appScale + 0.5f);
-                        rect.top = (int) (pos[1] * appScale + 0.5f);
-                        rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
-                        rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
+                        final Rect rect = getSourceBounds(v);
 
                         fillInIntent.setSourceBounds(rect);
                         handler.onClickHandler(v, pendingIntent, fillInIntent);
@@ -452,16 +453,7 @@
                             }
                             if (fillInIntent == null) return;
 
-                            final float appScale = view.getContext().getResources()
-                                    .getCompatibilityInfo().applicationScale;
-                            final int[] pos = new int[2];
-                            view.getLocationOnScreen(pos);
-
-                            final Rect rect = new Rect();
-                            rect.left = (int) (pos[0] * appScale + 0.5f);
-                            rect.top = (int) (pos[1] * appScale + 0.5f);
-                            rect.right = (int) ((pos[0] + view.getWidth()) * appScale + 0.5f);
-                            rect.bottom = (int) ((pos[1] + view.getHeight()) * appScale + 0.5f);
+                            final Rect rect = getSourceBounds(view);
 
                             final Intent intent = new Intent();
                             intent.setSourceBounds(rect);
@@ -679,33 +671,22 @@
                 }
             }
 
-            if (target != null) {
-                // If the pendingIntent is null, we clear the onClickListener
-                OnClickListener listener = null;
-                if (pendingIntent != null) {
-                    listener = new OnClickListener() {
-                        public void onClick(View v) {
-                            // Find target view location in screen coordinates and
-                            // fill into PendingIntent before sending.
-                            final float appScale = v.getContext().getResources()
-                                    .getCompatibilityInfo().applicationScale;
-                            final int[] pos = new int[2];
-                            v.getLocationOnScreen(pos);
+            // If the pendingIntent is null, we clear the onClickListener
+            OnClickListener listener = null;
+            if (pendingIntent != null) {
+                listener = new OnClickListener() {
+                    public void onClick(View v) {
+                        // Find target view location in screen coordinates and
+                        // fill into PendingIntent before sending.
+                        final Rect rect = getSourceBounds(v);
 
-                            final Rect rect = new Rect();
-                            rect.left = (int) (pos[0] * appScale + 0.5f);
-                            rect.top = (int) (pos[1] * appScale + 0.5f);
-                            rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
-                            rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
-
-                            final Intent intent = new Intent();
-                            intent.setSourceBounds(rect);
-                            handler.onClickHandler(v, pendingIntent, intent);
-                        }
-                    };
-                }
-                target.setOnClickListener(listener);
+                        final Intent intent = new Intent();
+                        intent.setSourceBounds(rect);
+                        handler.onClickHandler(v, pendingIntent, intent);
+                    }
+                };
             }
+            target.setOnClickListener(listener);
         }
 
         public String getActionName() {
@@ -717,6 +698,64 @@
         public final static int TAG = 1;
     }
 
+    private static Rect getSourceBounds(View v) {
+        final float appScale = v.getContext().getResources()
+                .getCompatibilityInfo().applicationScale;
+        final int[] pos = new int[2];
+        v.getLocationOnScreen(pos);
+
+        final Rect rect = new Rect();
+        rect.left = (int) (pos[0] * appScale + 0.5f);
+        rect.top = (int) (pos[1] * appScale + 0.5f);
+        rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
+        rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
+        return rect;
+    }
+
+    private static Method getMethod(View view, String methodName, Class<?> paramType) {
+        Method method;
+        Class<? extends View> klass = view.getClass();
+
+        synchronized (sMethodsLock) {
+            ArrayMap<String, Method> methods = sMethods.get(klass);
+            if (methods == null) {
+                methods = new ArrayMap<String, Method>();
+                sMethods.put(klass, methods);
+            }
+
+            method = methods.get(methodName);
+            if (method == null) {
+                try {
+                    method = klass.getMethod(methodName, paramType);
+                } catch (NoSuchMethodException ex) {
+                    throw new ActionException("view: " + klass.getName() + " doesn't have method: "
+                            + methodName + getParameters(paramType));
+                }
+
+                if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
+                    throw new ActionException("view: " + klass.getName()
+                            + " can't use method with RemoteViews: "
+                            + methodName + getParameters(paramType));
+                }
+
+                methods.put(methodName, method);
+            }
+        }
+
+        return method;
+    }
+
+    private static String getParameters(Class<?> paramType) {
+        if (paramType == null) return "()";
+        return "(" + paramType + ")";
+    }
+
+    private static Object[] wrapArg(Object value) {
+        Object[] args = sInvokeArgsTls.get();
+        args[0] = value;
+        return args;
+    }
+
     /**
      * Equivalent to calling a combination of {@link Drawable#setAlpha(int)},
      * {@link Drawable#setColorFilter(int, android.graphics.PorterDuff.Mode)},
@@ -836,28 +875,10 @@
             final View view = root.findViewById(viewId);
             if (view == null) return;
 
-            Class klass = view.getClass();
-            Method method;
             try {
-                method = klass.getMethod(this.methodName);
-            } catch (NoSuchMethodException ex) {
-                throw new ActionException("view: " + klass.getName() + " doesn't have method: "
-                        + this.methodName + "()");
-            }
-
-            if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
-                throw new ActionException("view: " + klass.getName()
-                        + " can't use method with RemoteViews: "
-                        + this.methodName + "()");
-            }
-
-            try {
-                //noinspection ConstantIfStatement
-                if (false) {
-                    Log.d(LOG_TAG, "view: " + klass.getName() + " calling method: "
-                        + this.methodName + "()");
-                }
-                method.invoke(view);
+                getMethod(view, this.methodName, null).invoke(view);
+            } catch (ActionException e) {
+                throw e;
             } catch (Exception ex) {
                 throw new ActionException(ex);
             }
@@ -1157,7 +1178,7 @@
             }
         }
 
-        private Class getParameterType() {
+        private Class<?> getParameterType() {
             switch (this.type) {
                 case BOOLEAN:
                     return boolean.class;
@@ -1197,37 +1218,16 @@
             final View view = root.findViewById(viewId);
             if (view == null) return;
 
-            Class param = getParameterType();
+            Class<?> param = getParameterType();
             if (param == null) {
                 throw new ActionException("bad type: " + this.type);
             }
 
-            Class klass = view.getClass();
-            Method method;
             try {
-                method = klass.getMethod(this.methodName, getParameterType());
-            }
-            catch (NoSuchMethodException ex) {
-                throw new ActionException("view: " + klass.getName() + " doesn't have method: "
-                        + this.methodName + "(" + param.getName() + ")");
-            }
-
-            if (!method.isAnnotationPresent(RemotableViewMethod.class)) {
-                throw new ActionException("view: " + klass.getName()
-                        + " can't use method with RemoteViews: "
-                        + this.methodName + "(" + param.getName() + ")");
-            }
-
-            try {
-                //noinspection ConstantIfStatement
-                if (false) {
-                    Log.d(LOG_TAG, "view: " + klass.getName() + " calling method: "
-                        + this.methodName + "(" + param.getName() + ") with "
-                        + (this.value == null ? "null" : this.value.getClass().getName()));
-                }
-                method.invoke(view, this.value);
-            }
-            catch (Exception ex) {
+                getMethod(view, this.methodName, param).invoke(view, wrapArg(this.value));
+            } catch (ActionException e) {
+                throw e;
+            } catch (Exception ex) {
                 throw new ActionException(ex);
             }
         }
@@ -1323,7 +1323,7 @@
         }
 
         public String getActionName() {
-            return "ViewGroupAction" + this.nestedViews == null ? "Remove" : "Add";
+            return "ViewGroupAction" + (nestedViews == null ? "Remove" : "Add");
         }
 
         public int mergeBehavior() {
@@ -1370,7 +1370,6 @@
 
         @Override
         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
-            final Context context = root.getContext();
             final TextView target = (TextView) root.findViewById(viewId);
             if (target == null) return;
             if (isRelative) {
@@ -1415,7 +1414,6 @@
 
         @Override
         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
-            final Context context = root.getContext();
             final TextView target = (TextView) root.findViewById(viewId);
             if (target == null) return;
             target.setTextSize(units, size);
@@ -1462,7 +1460,6 @@
 
         @Override
         public void apply(View root, ViewGroup rootParent, OnClickHandler handler) {
-            final Context context = root.getContext();
             final View target = root.findViewById(viewId);
             if (target == null) return;
             target.setPadding(left, top, right, bottom);
@@ -1494,6 +1491,7 @@
             return mMemoryUsage;
         }
 
+        @SuppressWarnings("deprecation")
         public void addBitmapMemory(Bitmap b) {
             final Bitmap.Config c = b.getConfig();
             // If we don't know, be pessimistic and assume 4
@@ -1597,7 +1595,7 @@
         if (mode == MODE_NORMAL) {
             mPackage = parcel.readString();
             mLayoutId = parcel.readInt();
-            mIsWidgetCollectionChild = parcel.readInt() == 1 ? true : false;
+            mIsWidgetCollectionChild = parcel.readInt() == 1;
 
             int count = parcel.readInt();
             if (count > 0) {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 3d361f1..6680393 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -1473,7 +1473,7 @@
         }
         mChildToScrollTo = null;
 
-        if (!hasLayout()) {
+        if (!isLaidOut()) {
             if (mSavedState != null) {
                 mScrollY = mSavedState.scrollPosition;
                 mSavedState = null;
diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl
index a9da863..2dc822e 100644
--- a/core/java/com/android/internal/app/IAppOpsService.aidl
+++ b/core/java/com/android/internal/app/IAppOpsService.aidl
@@ -24,10 +24,11 @@
     // be kept in sync with frameworks/native/include/binder/IAppOpsService.h
     int checkOperation(int code, int uid, String packageName);
     int noteOperation(int code, int uid, String packageName);
-    int startOperation(int code, int uid, String packageName);
-    void finishOperation(int code, int uid, String packageName);
+    int startOperation(IBinder token, int code, int uid, String packageName);
+    void finishOperation(IBinder token, int code, int uid, String packageName);
     void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
     void stopWatchingMode(IAppOpsCallback callback);
+    IBinder getToken(IBinder clientToken);
 
     // Remaining methods are only used in Java.
     List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
diff --git a/core/java/com/android/internal/app/RestrictionsPinActivity.java b/core/java/com/android/internal/app/RestrictionsPinActivity.java
index 57436f7..f8ce108 100644
--- a/core/java/com/android/internal/app/RestrictionsPinActivity.java
+++ b/core/java/com/android/internal/app/RestrictionsPinActivity.java
@@ -26,6 +26,7 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
+import android.view.WindowManager;
 import android.widget.EditText;
 import android.widget.TextView;
 import android.widget.TextView.OnEditorActionListener;
@@ -39,17 +40,24 @@
 public class RestrictionsPinActivity extends AlertActivity
         implements DialogInterface.OnClickListener, TextWatcher, OnEditorActionListener {
 
-    private UserManager mUserManager;
+    protected UserManager mUserManager;
+    protected boolean mHasRestrictionsPin;
 
-    private EditText mPin1Text;
-    private EditText mPin2Text;
-    private TextView mPinErrorMessage;
-    private TextView mPinMessage;
+    protected EditText mPinText;
+    protected TextView mPinErrorMessage;
+    protected TextView mPinMessage;
 
     @Override
     public void onCreate(Bundle icicle) {
         super.onCreate(icicle);
 
+        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
+        mHasRestrictionsPin = mUserManager.hasRestrictionsPin();
+        initUi();
+        setupAlert();
+    }
+
+    protected void initUi() {
         AlertController.AlertParams ap = mAlertParams;
         ap.mTitle = getString(R.string.restr_pin_enter_pin);
         ap.mPositiveButtonText = getString(R.string.ok);
@@ -58,18 +66,12 @@
         ap.mNegativeButtonListener = this;
         LayoutInflater inflater =
                 (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-        ap.mView = inflater.inflate(R.layout.pin_challenge, null);
+        ap.mView = inflater.inflate(R.layout.restrictions_pin_challenge, null);
 
         mPinMessage = (TextView) ap.mView.findViewById(R.id.pin_message);
-        mPin1Text = (EditText) ap.mView.findViewById(R.id.pin1_text);
-        mPin2Text = (EditText) ap.mView.findViewById(R.id.pin2_text);
+        mPinText = (EditText) ap.mView.findViewById(R.id.pin_text);
         mPinErrorMessage = (TextView) ap.mView.findViewById(R.id.pin_error_message);
-        mPin1Text.addTextChangedListener(this);
-        mPin2Text.addTextChangedListener(this);
-
-        mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
-
-        setupAlert();
+        mPinText.addTextChangedListener(this);
     }
 
     protected boolean verifyingPin() {
@@ -81,19 +83,12 @@
 
         setPositiveButtonState(false);
         boolean hasPin = mUserManager.hasRestrictionsPin();
-        if (verifyingPin()) {
-            if (hasPin) {
-                mPinMessage.setVisibility(View.GONE);
-                mPinErrorMessage.setVisibility(View.GONE);
-                mPin2Text.setVisibility(View.GONE);
-                mPin1Text.setOnEditorActionListener(this);
-                updatePinTimer(-1);
-            } else {
-                setResult(RESULT_OK);
-                finish();
-            }
-        } else if (hasPin) {
-            // Shouldn't really be in this state, exit
+        if (hasPin) {
+            mPinMessage.setVisibility(View.GONE);
+            mPinErrorMessage.setVisibility(View.GONE);
+            mPinText.setOnEditorActionListener(this);
+            updatePinTimer(-1);
+        } else if (verifyingPin()) {
             setResult(RESULT_OK);
             finish();
         }
@@ -114,14 +109,14 @@
                     seconds);
             mPinErrorMessage.setText(String.format(formatString, seconds));
             mPinErrorMessage.setVisibility(View.VISIBLE);
-            mPin1Text.setEnabled(false);
-            mPin1Text.setText("");
+            mPinText.setEnabled(false);
+            mPinText.setText("");
             setPositiveButtonState(false);
-            mPin1Text.postDelayed(mCountdownRunnable, Math.min(1000, pinTimerMs));
+            mPinText.postDelayed(mCountdownRunnable, Math.min(1000, pinTimerMs));
         } else {
             mPinErrorMessage.setVisibility(View.INVISIBLE);
-            mPin1Text.setEnabled(true);
-            mPin1Text.setText("");
+            mPinText.setEnabled(true);
+            mPinText.setText("");
         }
     }
 
@@ -134,20 +129,13 @@
         }
     }
 
-    private void performPositiveButtonAction() {
-        if (verifyingPin()) {
-            int result = mUserManager.checkRestrictionsPin(mPin1Text.getText().toString());
-            if (result == UserManager.PIN_VERIFICATION_SUCCESS) {
-                setResult(RESULT_OK);
-                finish();
-            } else if (result >= 0) {
-                updatePinTimer(result);
-            }
-        } else {
-            if (mUserManager.changeRestrictionsPin(mPin1Text.getText().toString())) {
-                setResult(RESULT_OK);
-                finish();
-            }
+    protected void performPositiveButtonAction() {
+        int result = mUserManager.checkRestrictionsPin(mPinText.getText().toString());
+        if (result == UserManager.PIN_VERIFICATION_SUCCESS) {
+            setResult(RESULT_OK);
+            finish();
+        } else if (result >= 0) {
+            updatePinTimer(result);
         }
     }
 
@@ -157,16 +145,8 @@
 
     @Override
     public void onTextChanged(CharSequence s, int start, int before, int count) {
-        CharSequence pin1 = mPin1Text.getText();
-        if (!verifyingPin()) {
-            CharSequence pin2 = mPin2Text.getText();
-            boolean match = pin1 != null && pin2 != null && pin1.length() >= 4
-                    && pin1.toString().equals(pin2.toString());
-            setPositiveButtonState(match);
-            mPinErrorMessage.setVisibility(match ? View.INVISIBLE : View.VISIBLE);
-        } else {
-            setPositiveButtonState(pin1 != null && pin1.length() >= 4);
-        }
+        CharSequence pin = mPinText.getText();
+        setPositiveButtonState(pin != null && pin.length() >= 4);
     }
 
     @Override
diff --git a/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java b/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java
index 35f2967..1d09292 100644
--- a/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java
+++ b/core/java/com/android/internal/app/RestrictionsPinSetupActivity.java
@@ -16,13 +16,115 @@
 
 package com.android.internal.app;
 
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.os.UserManager;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
 /**
  * This activity is launched by Settings and other apps to either create a new PIN or
- * challenge for an existing PIN. The PIN is maintained by UserManager.
+ * change an existing PIN. The PIN is maintained by UserManager.
  */
 public class RestrictionsPinSetupActivity extends RestrictionsPinActivity {
 
+    private EditText mNewPinText;
+    private EditText mConfirmPinText;
+
+    protected void initUi() {
+        AlertController.AlertParams ap = mAlertParams;
+        ap.mTitle = getString(R.string.restr_pin_enter_pin);
+        ap.mPositiveButtonText = getString(R.string.ok);
+        ap.mNegativeButtonText = getString(R.string.cancel);
+        ap.mPositiveButtonListener = this;
+        ap.mNegativeButtonListener = this;
+        LayoutInflater inflater =
+                (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        ap.mView = inflater.inflate(R.layout.restrictions_pin_setup, null);
+
+        mPinText = (EditText) ap.mView.findViewById(R.id.pin_text);
+        mPinMessage = (TextView) ap.mView.findViewById(R.id.pin_message);
+        mNewPinText = (EditText) ap.mView.findViewById(R.id.pin_new_text);
+        mConfirmPinText = (EditText) ap.mView.findViewById(R.id.pin_confirm_text);
+        mPinErrorMessage = (TextView) ap.mView.findViewById(R.id.pin_error_message);
+        mNewPinText.addTextChangedListener(this);
+        mConfirmPinText.addTextChangedListener(this);
+
+        if (!mHasRestrictionsPin) {
+            mPinText.setVisibility(View.GONE);
+        }
+    }
+
+    public void onResume() {
+        super.onResume();
+        setPositiveButtonState(false);
+    }
+
     protected boolean verifyingPin() {
         return false;
     }
+
+    private void setPositiveButtonState(boolean enabled) {
+        mAlert.getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(enabled);
+    }
+
+    public void onClick(DialogInterface dialog, int which) {
+        setResult(RESULT_CANCELED);
+        if (which == AlertDialog.BUTTON_POSITIVE) {
+            performPositiveButtonAction();
+        } else if (which == AlertDialog.BUTTON_NEGATIVE) {
+            finish();
+        }
+    }
+
+    protected void performPositiveButtonAction() {
+        if (mHasRestrictionsPin) {
+            int result = mUserManager.checkRestrictionsPin(mPinText.getText().toString());
+            if (result != UserManager.PIN_VERIFICATION_SUCCESS) {
+                // TODO: Set message that existing pin doesn't match
+                return;
+            }
+        }
+        if (mUserManager.changeRestrictionsPin(mNewPinText.getText().toString())) {
+            // TODO: Send message to PIN recovery agent about the recovery email address
+            setResult(RESULT_OK);
+            finish();
+        }
+    }
+
+    @Override
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+        CharSequence pin = mPinText.getText();
+        CharSequence pin1 = mNewPinText.getText();
+        CharSequence pin2 = mConfirmPinText.getText();
+        boolean match = pin1 != null && pin2 != null && pin1.length() >= 4
+                && pin1.toString().equals(pin2.toString())
+                && (!mHasRestrictionsPin || (pin != null && pin.length() >= 4));
+        boolean showError = !TextUtils.isEmpty(pin1) && !TextUtils.isEmpty(pin2);
+        // TODO: Check recovery email address as well
+        setPositiveButtonState(match);
+        mPinErrorMessage.setVisibility((match || !showError) ? View.INVISIBLE : View.VISIBLE);
+    }
+
+    @Override
+    public void afterTextChanged(Editable s) {
+    }
+
+    @Override
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        performPositiveButtonAction();
+        return true;
+    }
 }
diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java
index 956653b..abf99a3 100644
--- a/core/java/com/android/internal/net/VpnConfig.java
+++ b/core/java/com/android/internal/net/VpnConfig.java
@@ -21,10 +21,14 @@
 import android.content.Intent;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.net.RouteInfo;
+import android.net.LinkAddress;
 
 import com.android.internal.util.Preconditions;
 
+import java.net.InetAddress;
 import java.util.List;
+import java.util.ArrayList;
 
 /**
  * A simple container used to carry information in VpnBuilder, VpnDialogs,
@@ -61,14 +65,42 @@
     public String interfaze;
     public String session;
     public int mtu = -1;
-    public String addresses;
-    public String routes;
+    public List<LinkAddress> addresses = new ArrayList<LinkAddress>();
+    public List<RouteInfo> routes = new ArrayList<RouteInfo>();
     public List<String> dnsServers;
     public List<String> searchDomains;
     public PendingIntent configureIntent;
     public long startTime = -1;
     public boolean legacy;
 
+    public void addLegacyRoutes(String routesStr) {
+        if (routesStr.trim().equals("")) {
+            return;
+        }
+        String[] routes = routesStr.trim().split(" ");
+        for (String route : routes) {
+            //each route is ip/prefix
+            String[] split = route.split("/");
+            RouteInfo info = new RouteInfo(new LinkAddress
+                    (InetAddress.parseNumericAddress(split[0]), Integer.parseInt(split[1])), null);
+            this.routes.add(info);
+        }
+    }
+
+    public void addLegacyAddresses(String addressesStr) {
+        if (addressesStr.trim().equals("")) {
+            return;
+        }
+        String[] addresses = addressesStr.trim().split(" ");
+        for (String address : addresses) {
+            //each address is ip/prefix
+            String[] split = address.split("/");
+            LinkAddress addr = new LinkAddress(InetAddress.parseNumericAddress(split[0]),
+                    Integer.parseInt(split[1]));
+            this.addresses.add(addr);
+        }
+    }
+
     @Override
     public int describeContents() {
         return 0;
@@ -80,8 +112,8 @@
         out.writeString(interfaze);
         out.writeString(session);
         out.writeInt(mtu);
-        out.writeString(addresses);
-        out.writeString(routes);
+        out.writeTypedList(addresses);
+        out.writeTypedList(routes);
         out.writeStringList(dnsServers);
         out.writeStringList(searchDomains);
         out.writeParcelable(configureIntent, flags);
@@ -98,8 +130,8 @@
             config.interfaze = in.readString();
             config.session = in.readString();
             config.mtu = in.readInt();
-            config.addresses = in.readString();
-            config.routes = in.readString();
+            in.readTypedList(config.addresses, LinkAddress.CREATOR);
+            in.readTypedList(config.routes, RouteInfo.CREATOR);
             config.dnsServers = in.createStringArrayList();
             config.searchDomains = in.createStringArrayList();
             config.configureIntent = in.readParcelable(null);
diff --git a/core/java/com/android/internal/util/FastPrintWriter.java b/core/java/com/android/internal/util/FastPrintWriter.java
index fa53cfa..9bec10e 100644
--- a/core/java/com/android/internal/util/FastPrintWriter.java
+++ b/core/java/com/android/internal/util/FastPrintWriter.java
@@ -458,7 +458,7 @@
         if (lnum == 0) {
             println("0");
         } else {
-            super.print(lnum);
+            super.println(lnum);
         }
     }
 
diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java
index fa35308..f40f48c 100644
--- a/core/java/com/android/internal/util/XmlUtils.java
+++ b/core/java/com/android/internal/util/XmlUtils.java
@@ -796,16 +796,8 @@
             }
             throw new XmlPullParserException(
                 "Unexpected end of document in <string>");
-        } else if (tagName.equals("int")) {
-            res = Integer.parseInt(parser.getAttributeValue(null, "value"));
-        } else if (tagName.equals("long")) {
-            res = Long.valueOf(parser.getAttributeValue(null, "value"));
-        } else if (tagName.equals("float")) {
-            res = new Float(parser.getAttributeValue(null, "value"));
-        } else if (tagName.equals("double")) {
-            res = new Double(parser.getAttributeValue(null, "value"));
-        } else if (tagName.equals("boolean")) {
-            res = Boolean.valueOf(parser.getAttributeValue(null, "value"));
+        } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
+            // all work already done by readThisPrimitiveValueXml
         } else if (tagName.equals("int-array")) {
             parser.next();
             res = readThisIntArrayXml(parser, "int-array", name);
@@ -858,6 +850,31 @@
             "Unexpected end of document in <" + tagName + ">");
     }
 
+    private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName)
+    throws XmlPullParserException, java.io.IOException
+    {
+        try {
+            if (tagName.equals("int")) {
+                return Integer.parseInt(parser.getAttributeValue(null, "value"));
+            } else if (tagName.equals("long")) {
+                return Long.valueOf(parser.getAttributeValue(null, "value"));
+            } else if (tagName.equals("float")) {
+                return new Float(parser.getAttributeValue(null, "value"));
+            } else if (tagName.equals("double")) {
+                return new Double(parser.getAttributeValue(null, "value"));
+            } else if (tagName.equals("boolean")) {
+                return Boolean.valueOf(parser.getAttributeValue(null, "value"));
+            } else {
+                return null;
+            }
+        } catch (NullPointerException e) {
+            throw new XmlPullParserException("Need value attribute in <" + tagName + ">");
+        } catch (NumberFormatException e) {
+            throw new XmlPullParserException(
+                    "Not a number in value attribute in <" + tagName + ">");
+        }
+    }
+
     public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
     {
         int type;
diff --git a/core/java/com/android/internal/view/ActionBarPolicy.java b/core/java/com/android/internal/view/ActionBarPolicy.java
index cf69a9d..25086c5 100644
--- a/core/java/com/android/internal/view/ActionBarPolicy.java
+++ b/core/java/com/android/internal/view/ActionBarPolicy.java
@@ -45,10 +45,7 @@
     }
 
     public boolean showsOverflowMenuButton() {
-        return !ViewConfiguration.get(mContext).hasPermanentMenuKey() ||
-                ((mContext.getResources().getConfiguration().uiMode &
-                        Configuration.UI_MODE_TYPE_TELEVISION) ==
-                        Configuration.UI_MODE_TYPE_TELEVISION);
+        return true;
     }
 
     public int getEmbeddedMenuWidthLimit() {
diff --git a/core/java/com/android/internal/view/menu/ListMenuItemView.java b/core/java/com/android/internal/view/menu/ListMenuItemView.java
index 464ae2f..df579c69 100644
--- a/core/java/com/android/internal/view/menu/ListMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ListMenuItemView.java
@@ -26,41 +26,48 @@
 import android.widget.CheckBox;
 import android.widget.CompoundButton;
 import android.widget.ImageView;
+import android.widget.LinearLayout;
 import android.widget.RadioButton;
-import android.widget.RelativeLayout;
 import android.widget.TextView;
 
 /**
  * The item view for each item in the ListView-based MenuViews.
  */
-public class ListMenuItemView extends RelativeLayout implements MenuView.ItemView {
-    private final Drawable mBackground;
-    private final int mTextAppearance;
-
-    private MenuItemImpl mItemData;
-
+public class ListMenuItemView extends LinearLayout implements MenuView.ItemView {
+    private static final String TAG = "ListMenuItemView";
+    private MenuItemImpl mItemData; 
+    
     private ImageView mIconView;
     private RadioButton mRadioButton;
     private TextView mTitleView;
     private CheckBox mCheckBox;
     private TextView mShortcutView;
-
+    
+    private Drawable mBackground;
+    private int mTextAppearance;
+    private Context mTextAppearanceContext;
+    private boolean mPreserveIconSpacing;
+    
+    private int mMenuType;
+    
     private LayoutInflater mInflater;
 
-    private boolean mPreserveIconSpacing;
     private boolean mForceShowIcon;
 
     public ListMenuItemView(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs);
-
-        final TypedArray a = context.obtainStyledAttributes(
+    
+        TypedArray a =
+            context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.MenuView, defStyle, 0);
+        
         mBackground = a.getDrawable(com.android.internal.R.styleable.MenuView_itemBackground);
-        mTextAppearance = a.getResourceId(
-                com.android.internal.R.styleable.MenuView_itemTextAppearance, 0);
+        mTextAppearance = a.getResourceId(com.android.internal.R.styleable.
+                                          MenuView_itemTextAppearance, -1);
         mPreserveIconSpacing = a.getBoolean(
                 com.android.internal.R.styleable.MenuView_preserveIconSpacing, false);
-
+        mTextAppearanceContext = context;
+        
         a.recycle();
     }
 
@@ -71,22 +78,24 @@
     @Override
     protected void onFinishInflate() {
         super.onFinishInflate();
-
-        setBackground(mBackground);
-
+        
+        setBackgroundDrawable(mBackground);
+        
         mTitleView = (TextView) findViewById(com.android.internal.R.id.title);
-        if (mTextAppearance != 0) {
-            mTitleView.setTextAppearance(mContext, mTextAppearance);
+        if (mTextAppearance != -1) {
+            mTitleView.setTextAppearance(mTextAppearanceContext,
+                                         mTextAppearance);
         }
-
+        
         mShortcutView = (TextView) findViewById(com.android.internal.R.id.shortcut);
     }
 
-    @Override
     public void initialize(MenuItemImpl itemData, int menuType) {
         mItemData = itemData;
+        mMenuType = menuType;
 
         setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
+        
         setTitle(itemData.getTitleForItemView(this));
         setCheckable(itemData.isCheckable());
         setShortcut(itemData.shouldShowShortcut(), itemData.getShortcut());
@@ -98,36 +107,29 @@
         mPreserveIconSpacing = mForceShowIcon = forceShow;
     }
 
-    @Override
     public void setTitle(CharSequence title) {
         if (title != null) {
             mTitleView.setText(title);
-
-            if (mTitleView.getVisibility() != VISIBLE) {
-                mTitleView.setVisibility(VISIBLE);
-            }
+            
+            if (mTitleView.getVisibility() != VISIBLE) mTitleView.setVisibility(VISIBLE);
         } else {
-            if (mTitleView.getVisibility() != GONE) {
-                mTitleView.setVisibility(GONE);
-            }
+            if (mTitleView.getVisibility() != GONE) mTitleView.setVisibility(GONE);
         }
     }
-
-    @Override
+    
     public MenuItemImpl getItemData() {
         return mItemData;
     }
 
-    @Override
     public void setCheckable(boolean checkable) {
         if (!checkable && mRadioButton == null && mCheckBox == null) {
             return;
         }
-
+        
         // Depending on whether its exclusive check or not, the checkbox or
         // radio button will be the one in use (and the other will be otherCompoundButton)
         final CompoundButton compoundButton;
-        final CompoundButton otherCompoundButton;
+        final CompoundButton otherCompoundButton; 
 
         if (mItemData.isExclusiveCheckable()) {
             if (mRadioButton == null) {
@@ -142,36 +144,28 @@
             compoundButton = mCheckBox;
             otherCompoundButton = mRadioButton;
         }
-
+        
         if (checkable) {
             compoundButton.setChecked(mItemData.isChecked());
-
+            
             final int newVisibility = checkable ? VISIBLE : GONE;
             if (compoundButton.getVisibility() != newVisibility) {
                 compoundButton.setVisibility(newVisibility);
             }
-
-            // Align text to the start of the visible compound button.
-            alignTextToStartOf(compoundButton);
-
+            
             // Make sure the other compound button isn't visible
             if (otherCompoundButton != null && otherCompoundButton.getVisibility() != GONE) {
                 otherCompoundButton.setVisibility(GONE);
             }
         } else {
-            if (mCheckBox != null) {
-                mCheckBox.setVisibility(GONE);
-            }
-            if (mRadioButton != null) {
-                mRadioButton.setVisibility(GONE);
-            }
+            if (mCheckBox != null) mCheckBox.setVisibility(GONE);
+            if (mRadioButton != null) mRadioButton.setVisibility(GONE);
         }
     }
-
-    @Override
+    
     public void setChecked(boolean checked) {
         CompoundButton compoundButton;
-
+        
         if (mItemData.isExclusiveCheckable()) {
             if (mRadioButton == null) {
                 insertRadioButton();
@@ -183,13 +177,13 @@
             }
             compoundButton = mCheckBox;
         }
-
+        
         compoundButton.setChecked(checked);
     }
 
-    @Override
     public void setShortcut(boolean showShortcut, char shortcutKey) {
-        final int newVisibility = (showShortcut && mItemData.shouldShowShortcut()) ? VISIBLE : GONE;
+        final int newVisibility = (showShortcut && mItemData.shouldShowShortcut())
+                ? VISIBLE : GONE;
 
         if (newVisibility == VISIBLE) {
             mShortcutView.setText(mItemData.getShortcutLabel());
@@ -199,22 +193,21 @@
             mShortcutView.setVisibility(newVisibility);
         }
     }
-
-    @Override
+    
     public void setIcon(Drawable icon) {
         final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon;
         if (!showIcon && !mPreserveIconSpacing) {
             return;
         }
-
+        
         if (mIconView == null && icon == null && !mPreserveIconSpacing) {
             return;
         }
-
+        
         if (mIconView == null) {
             insertIconView();
         }
-
+        
         if (icon != null || mPreserveIconSpacing) {
             mIconView.setImageDrawable(showIcon ? icon : null);
 
@@ -225,52 +218,51 @@
             mIconView.setVisibility(GONE);
         }
     }
-
+    
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         if (mIconView != null && mPreserveIconSpacing) {
             // Enforce minimum icon spacing
-            final ViewGroup.LayoutParams lp = getLayoutParams();
-            final LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
+            ViewGroup.LayoutParams lp = getLayoutParams();
+            LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
             if (lp.height > 0 && iconLp.width <= 0) {
                 iconLp.width = lp.height;
             }
         }
-
         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
     }
 
     private void insertIconView() {
-        mIconView = (ImageView) getInflater()
-                .inflate(com.android.internal.R.layout.list_menu_item_icon, this, true);
+        LayoutInflater inflater = getInflater();
+        mIconView = (ImageView) inflater.inflate(com.android.internal.R.layout.list_menu_item_icon,
+                this, false);
+        addView(mIconView, 0);
     }
-
+    
     private void insertRadioButton() {
-        mRadioButton = (RadioButton) getInflater()
-                .inflate(com.android.internal.R.layout.list_menu_item_radio, this, true);
+        LayoutInflater inflater = getInflater();
+        mRadioButton =
+                (RadioButton) inflater.inflate(com.android.internal.R.layout.list_menu_item_radio,
+                this, false);
+        addView(mRadioButton);
     }
-
+    
     private void insertCheckBox() {
-        mCheckBox = (CheckBox) getInflater()
-                .inflate(com.android.internal.R.layout.list_menu_item_checkbox, this, true);
+        LayoutInflater inflater = getInflater();
+        mCheckBox =
+                (CheckBox) inflater.inflate(com.android.internal.R.layout.list_menu_item_checkbox,
+                this, false);
+        addView(mCheckBox);
     }
 
-    private void alignTextToStartOf(View v) {
-        final LayoutParams params = (LayoutParams) mTitleView.getLayoutParams();
-        params.addRule(RelativeLayout.START_OF, v.getId());
-        mTitleView.setLayoutParams(params);
-    }
-
-    @Override
     public boolean prefersCondensedTitle() {
         return false;
     }
 
-    @Override
     public boolean showsIcon() {
         return mForceShowIcon;
     }
-
+    
     private LayoutInflater getInflater() {
         if (mInflater == null) {
             mInflater = LayoutInflater.from(mContext);
diff --git a/core/java/com/android/internal/view/menu/MenuItemImpl.java b/core/java/com/android/internal/view/menu/MenuItemImpl.java
index 39078ca..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 ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0 || mActionView == null) {
+        if (!hasCollapsibleActionView()) {
             return false;
         }
 
@@ -653,7 +653,13 @@
     }
 
     public boolean hasCollapsibleActionView() {
-        return (mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0 && mActionView != null;
+        if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0) {
+            if (mActionView == null && mActionProvider != null) {
+                mActionView = mActionProvider.onCreateActionView(this);
+            }
+            return mActionView != null;
+        }
+        return false;
     }
 
     public void setActionViewExpanded(boolean isExpanded) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 20e5011..d60fa18 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -196,6 +196,7 @@
 	libgui \
 	libinput \
 	libcamera_client \
+	libcamera_metadata \
 	libskia \
 	libsqlite \
 	libEGL \
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 63683b4..b03d12a 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -271,6 +271,26 @@
     return true;

 }

 

+static void Bitmap_reconfigure(JNIEnv* env, jobject clazz, jint bitmapInt,

+        int width, int height, SkBitmap::Config config, int allocSize) {

+    if (width * height * SkBitmap::ComputeBytesPerPixel(config) > allocSize) {

+        // done in native as there's no way to get BytesPerPixel in Java

+        doThrowIAE(env, "Bitmap not large enough to support new configuration");

+        return;

+    }

+    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapInt);

+    SkPixelRef* ref = bitmap->pixelRef();

+    SkSafeRef(ref);

+    bitmap->setConfig(config, width, height);

+    bitmap->setPixelRef(ref);

+

+    // notifyPixelsChanged will increment the generation ID even though the actual pixel data

+    // hasn't been touched. This signals the renderer that the bitmap (including width, height,

+    // and config) has changed.

+    ref->notifyPixelsChanged();

+    SkSafeUnref(ref);

+}

+

 // These must match the int values in Bitmap.java

 enum JavaEncodeFormat {

     kJPEG_JavaEncodeFormat = 0,

@@ -666,6 +686,7 @@
         (void*)Bitmap_copy },

     {   "nativeDestructor",         "(I)V", (void*)Bitmap_destructor },

     {   "nativeRecycle",            "(I)Z", (void*)Bitmap_recycle },

+    {   "nativeReconfigure",        "(IIIII)V", (void*)Bitmap_reconfigure },

     {   "nativeCompress",           "(IIILjava/io/OutputStream;[B)Z",

         (void*)Bitmap_compress },

     {   "nativeErase",              "(II)V", (void*)Bitmap_erase },

diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 016c11e..f58f35d 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -65,6 +65,7 @@
         { SkImageDecoder::kICO_Format,  "image/x-ico" },
         { SkImageDecoder::kJPEG_Format, "image/jpeg" },
         { SkImageDecoder::kPNG_Format,  "image/png" },
+        { SkImageDecoder::kWEBP_Format, "image/webp" },
         { SkImageDecoder::kWBMP_Format, "image/vnd.wap.wbmp" }
     };
 
@@ -116,30 +117,6 @@
     }
 }
 
-static jbyteArray nativeScaleNinePatch(JNIEnv* env, jobject, jbyteArray chunkObject, jfloat scale,
-        jobject padding) {
-
-    jbyte* array = env->GetByteArrayElements(chunkObject, 0);
-    if (array != NULL) {
-        size_t chunkSize = env->GetArrayLength(chunkObject);
-        void* storage = alloca(chunkSize);
-        android::Res_png_9patch* chunk = static_cast<android::Res_png_9patch*>(storage);
-        memcpy(chunk, array, chunkSize);
-        android::Res_png_9patch::deserialize(chunk);
-
-        scaleNinePatchChunk(chunk, scale);
-        memcpy(array, chunk, chunkSize);
-
-        if (padding) {
-            GraphicsJNI::set_jrect(env, padding, chunk->paddingLeft, chunk->paddingTop,
-                    chunk->paddingRight, chunk->paddingBottom);
-        }
-
-        env->ReleaseByteArrayElements(chunkObject, array, 0);
-    }
-    return chunkObject;
-}
-
 static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream,
         int sampleSize, bool ditherImage) {
 
@@ -624,11 +601,6 @@
         (void*)nativeDecodeByteArray
     },
 
-    {   "nativeScaleNinePatch",
-        "([BFLandroid/graphics/Rect;)[B",
-        (void*)nativeScaleNinePatch
-    },
-
     {   "nativeIsSeekable",
         "(Ljava/io/FileDescriptor;)Z",
         (void*)nativeIsSeekable
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index 753f5dc..bf9177b 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -197,10 +197,10 @@
     }
 }
 
-static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jint texName,
-        jobject weakThiz, jboolean allowSynchronous)
+static void SurfaceTexture_init(JNIEnv* env, jobject thiz, jint texName, jobject weakThiz)
 {
-    sp<GLConsumer> surfaceTexture(new GLConsumer(texName, allowSynchronous));
+    sp<BufferQueue> bq = new BufferQueue();
+    sp<GLConsumer> surfaceTexture(new GLConsumer(bq, texName, GL_TEXTURE_EXTERNAL_OES, true, true));
     if (surfaceTexture == 0) {
         jniThrowException(env, OutOfResourcesException,
                 "Unable to create native SurfaceTexture");
@@ -285,7 +285,7 @@
 
 static JNINativeMethod gSurfaceTextureMethods[] = {
     {"nativeClassInit",            "()V",   (void*)SurfaceTexture_classInit },
-    {"nativeInit",                 "(ILjava/lang/Object;Z)V", (void*)SurfaceTexture_init },
+    {"nativeInit",                 "(ILjava/lang/Object;)V", (void*)SurfaceTexture_init },
     {"nativeFinalize",             "()V",   (void*)SurfaceTexture_finalize },
     {"nativeSetDefaultBufferSize", "(II)V", (void*)SurfaceTexture_setDefaultBufferSize },
     {"nativeUpdateTexImage",       "()V",   (void*)SurfaceTexture_updateTexImage },
diff --git a/core/jni/android_hardware_photography_CameraMetadata.cpp b/core/jni/android_hardware_photography_CameraMetadata.cpp
index fa363f3..5190a37 100644
--- a/core/jni/android_hardware_photography_CameraMetadata.cpp
+++ b/core/jni/android_hardware_photography_CameraMetadata.cpp
@@ -16,6 +16,7 @@
 */
 
 // #define LOG_NDEBUG 0
+// #define LOG_NNDEBUG 0
 #define LOG_TAG "CameraMetadata-JNI"
 #include <utils/Log.h>
 
@@ -25,6 +26,16 @@
 #include "android_runtime/AndroidRuntime.h"
 
 #include <camera/CameraMetadata.h>
+#include <nativehelper/ScopedUtfChars.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
+
+#if defined(LOG_NNDEBUG)
+#if !LOG_NNDEBUG
+#define ALOGVV ALOGV
+#endif
+#else
+#define ALOGVV(...)
+#endif
 
 // fully-qualified class name
 #define CAMERA_METADATA_CLASS_NAME "android/hardware/photography/CameraMetadata"
@@ -37,9 +48,70 @@
 
 static fields_t fields;
 
+namespace {
+struct Helpers {
+    static size_t getTypeSize(uint8_t type) {
+        if (type >= NUM_TYPES) {
+            ALOGE("%s: Invalid type specified (%ud)", __FUNCTION__, type);
+            return static_cast<size_t>(-1);
+        }
+
+        return camera_metadata_type_size[type];
+    }
+
+    static status_t updateAny(CameraMetadata *metadata,
+                          uint32_t tag,
+                          uint32_t type,
+                          const void *data,
+                          size_t dataBytes) {
+
+        if (type >= NUM_TYPES) {
+            ALOGE("%s: Invalid type specified (%ud)", __FUNCTION__, type);
+            return INVALID_OPERATION;
+        }
+
+        size_t typeSize = getTypeSize(type);
+
+        if (dataBytes % typeSize != 0) {
+            ALOGE("%s: Expected dataBytes (%ud) to be divisible by typeSize "
+                  "(%ud)", __FUNCTION__, dataBytes, typeSize);
+            return BAD_VALUE;
+        }
+
+        size_t dataCount = dataBytes / typeSize;
+
+        switch(type) {
+#define METADATA_UPDATE(runtime_type, compile_type)                            \
+            case runtime_type: {                                               \
+                const compile_type *dataPtr =                                  \
+                        static_cast<const compile_type*>(data);                \
+                return metadata->update(tag, dataPtr, dataCount);              \
+            }                                                                  \
+
+            METADATA_UPDATE(TYPE_BYTE,     uint8_t);
+            METADATA_UPDATE(TYPE_INT32,    int32_t);
+            METADATA_UPDATE(TYPE_FLOAT,    float);
+            METADATA_UPDATE(TYPE_INT64,    int64_t);
+            METADATA_UPDATE(TYPE_DOUBLE,   double);
+            METADATA_UPDATE(TYPE_RATIONAL, camera_metadata_rational_t);
+
+            default: {
+                // unreachable
+                ALOGE("%s: Unreachable", __FUNCTION__);
+                return INVALID_OPERATION;
+            }
+        }
+
+#undef METADATA_UPDATE
+    }
+};
+} // namespace {}
+
 extern "C" {
 
 static void CameraMetadata_classInit(JNIEnv *env, jobject thiz);
+static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName);
+static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag);
 
 // Less safe access to native pointer. Does NOT throw any Java exceptions if NULL.
 static CameraMetadata* CameraMetadata_getPointerNoThrow(JNIEnv *env, jobject thiz) {
@@ -140,6 +212,94 @@
     metadata->swap(*otherMetadata);
 }
 
+static jbyteArray CameraMetadata_readValues(JNIEnv *env, jobject thiz, jint tag) {
+    ALOGV("%s (tag = %d)", __FUNCTION__, tag);
+
+    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+    if (metadata == NULL) return NULL;
+
+    int tagType = get_camera_metadata_tag_type(tag);
+    if (tagType == -1) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Tag (%d) did not have a type", tag);
+        return NULL;
+    }
+    size_t tagSize = Helpers::getTypeSize(tagType);
+
+    camera_metadata_entry entry = metadata->find(tag);
+    if (entry.count == 0) {
+         if (!metadata->exists(tag)) {
+             ALOGV("%s: Tag %d does not have any entries", __FUNCTION__, tag);
+             return NULL;
+         } else {
+             // OK: we will return a 0-sized array.
+             ALOGV("%s: Tag %d had an entry, but it had 0 data", __FUNCTION__,
+                   tag);
+         }
+    }
+
+    jsize byteCount = entry.count * tagSize;
+    jbyteArray byteArray = env->NewByteArray(byteCount);
+    if (env->ExceptionCheck()) return NULL;
+
+    // Copy into java array from native array
+    ScopedByteArrayRW arrayWriter(env, byteArray);
+    memcpy(arrayWriter.get(), entry.data.u8, byteCount);
+
+    return byteArray;
+}
+
+static void CameraMetadata_writeValues(JNIEnv *env, jobject thiz, jint tag, jbyteArray src) {
+    ALOGV("%s (tag = %d)", __FUNCTION__, tag);
+
+    CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
+    if (metadata == NULL) return;
+
+    int tagType = get_camera_metadata_tag_type(tag);
+    if (tagType == -1) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Tag (%d) did not have a type", tag);
+        return;
+    }
+    size_t tagSize = Helpers::getTypeSize(tagType);
+
+    status_t res;
+
+    if (src == NULL) {
+        // If array is NULL, delete the entry
+        if (metadata->exists(tag)) {
+            res = metadata->erase(tag);
+            ALOGV("%s: Erase values (res = %d)", __FUNCTION__, res);
+        } else {
+            res = OK;
+            ALOGV("%s: Don't need to erase", __FUNCTION__);
+        }
+    } else {
+        // Copy from java array into native array
+        ScopedByteArrayRO arrayReader(env, src);
+        if (arrayReader.get() == NULL) return;
+
+        res = Helpers::updateAny(metadata, static_cast<uint32_t>(tag),
+                                 tagType, arrayReader.get(), arrayReader.size());
+
+        ALOGV("%s: Update values (res = %d)", __FUNCTION__, res);
+    }
+
+    if (res == OK) {
+        return;
+    } else if (res == BAD_VALUE) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Src byte array was poorly formed");
+    } else if (res == INVALID_OPERATION) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                             "Internal error while trying to update metadata");
+    } else {
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                             "Unknown error (%d) while trying to update "
+                            "metadata", res);
+    }
+}
+
 static void CameraMetadata_readFromParcel(JNIEnv *env, jobject thiz, jobject parcel) {
     ALOGV("%s", __FUNCTION__);
     CameraMetadata* metadata = CameraMetadata_getPointerThrow(env, thiz);
@@ -187,9 +347,17 @@
 //-------------------------------------------------
 
 static JNINativeMethod gCameraMetadataMethods[] = {
+// static methods
   { "nativeClassInit",
     "()V",
     (void *)CameraMetadata_classInit },
+  { "nativeGetTagFromKey",
+    "(Ljava/lang/String;)I",
+    (void *)CameraMetadata_getTagFromKey },
+  { "nativeGetTypeFromTag",
+    "(I)I",
+    (void *)CameraMetadata_getTypeFromTag },
+// instance methods
   { "nativeAllocate",
     "()J",
     (void*)CameraMetadata_allocate },
@@ -205,6 +373,13 @@
   { "nativeSwap",
     "(L" CAMERA_METADATA_CLASS_NAME ";)V",
     (void *)CameraMetadata_swap },
+  { "nativeReadValues",
+    "(I)[B",
+    (void *)CameraMetadata_readValues },
+  { "nativeWriteValues",
+    "(I[B)V",
+    (void *)CameraMetadata_writeValues },
+// Parcelable interface
   { "nativeReadFromParcel",
     "(Landroid/os/Parcel;)V",
     (void *)CameraMetadata_readFromParcel },
@@ -268,4 +443,99 @@
 
     jclass clazz = env->FindClass(CAMERA_METADATA_CLASS_NAME);
 }
+
+static jint CameraMetadata_getTagFromKey(JNIEnv *env, jobject thiz, jstring keyName) {
+
+    ScopedUtfChars keyScoped(env, keyName);
+    const char *key = keyScoped.c_str();
+    if (key == NULL) {
+        // exception thrown by ScopedUtfChars
+        return 0;
+    }
+    size_t keyLength = strlen(key);
+
+    ALOGV("%s (key = '%s')", __FUNCTION__, key);
+
+    // First, find the section by the longest string match
+    const char *section = NULL;
+    size_t sectionIndex = 0;
+    size_t sectionLength = 0;
+    for (size_t i = 0; i < ANDROID_SECTION_COUNT; ++i) {
+        const char *str = camera_metadata_section_names[i];
+        ALOGVV("%s: Trying to match against section '%s'",
+               __FUNCTION__, str);
+        if (strstr(key, str) == key) { // key begins with the section name
+            size_t strLength = strlen(str);
+
+            ALOGVV("%s: Key begins with section name", __FUNCTION__);
+
+            // section name is the longest we've found so far
+            if (section == NULL || sectionLength < strLength) {
+                section = str;
+                sectionIndex = i;
+                sectionLength = strLength;
+
+                ALOGVV("%s: Found new best section (idx %d)", __FUNCTION__, sectionIndex);
+            }
+        }
+    }
+
+    // TODO: vendor ext
+    // TODO: Make above get_camera_metadata_section_from_name ?
+
+    if (section == NULL) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Could not find section name for key '%s')", key);
+        return 0;
+    } else {
+        ALOGV("%s: Found matched section '%s' (%d)",
+              __FUNCTION__, section, sectionIndex);
+    }
+
+    // Get the tag name component of the key
+    const char *keyTagName = key + sectionLength + 1; // x.y.z -> z
+    if (sectionLength + 1 >= keyLength) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Key length too short for key '%s')", key);
+    }
+
+    // Match rest of name against the tag names in that section only
+    uint32_t tagBegin, tagEnd; // [tagBegin, tagEnd)
+    tagBegin = camera_metadata_section_bounds[sectionIndex][0];
+    tagEnd = camera_metadata_section_bounds[sectionIndex][1];
+
+    uint32_t tag;
+    for (tag = tagBegin; tag < tagEnd; ++tag) {
+        const char *tagName = get_camera_metadata_tag_name(tag);
+
+        if (strcmp(keyTagName, tagName) == 0) {
+            ALOGV("%s: Found matched tag '%s' (%d)",
+                  __FUNCTION__, tagName, tag);
+            break;
+        }
+    }
+
+    // TODO: vendor ext
+    // TODO: Make above get_camera_metadata_tag_from_name ?
+
+    if (tag == tagEnd) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Could not find tag name for key '%s')", key);
+        return 0;
+    }
+
+    return tag;
+}
+
+static jint CameraMetadata_getTypeFromTag(JNIEnv *env, jobject thiz, jint tag) {
+    int tagType = get_camera_metadata_tag_type(tag);
+    if (tagType == -1) {
+        jniThrowExceptionFmt(env, "java/lang/IllegalArgumentException",
+                             "Tag (%d) did not have a type", tag);
+        return -1;
+    }
+
+    return tagType;
+}
+
 } // extern "C"
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index 67c2cfd..139b7be 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -117,10 +117,36 @@
     return env->NewStringUTF(AudioSystem::getParameters(0, c_keys8).string());
 }
 
+static JNIEnv* AudioSystem_getJNIEnv(bool* needsDetach) {
+    *needsDetach = false;
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    if (env == NULL) {
+        JavaVMAttachArgs args = {JNI_VERSION_1_4, NULL, NULL};
+        JavaVM* vm = AndroidRuntime::getJavaVM();
+        int result = vm->AttachCurrentThread(&env, (void*) &args);
+        if (result != JNI_OK) {
+            ALOGE("thread attach failed: %#x", result);
+            return NULL;
+        }
+        *needsDetach = true;
+    }
+    return env;
+}
+
+static void AudioSystem_detachJNI() {
+    JavaVM* vm = AndroidRuntime::getJavaVM();
+    int result = vm->DetachCurrentThread();
+    if (result != JNI_OK) {
+        ALOGE("thread detach failed: %#x", result);
+    }
+}
+
 static void
 android_media_AudioSystem_error_callback(status_t err)
 {
-    JNIEnv *env = AndroidRuntime::getJNIEnv();
+    bool needsDetach = false;
+    JNIEnv *env = AudioSystem_getJNIEnv(&needsDetach);
+
     if (env == NULL) {
         return;
     }
@@ -142,6 +168,10 @@
     }
 
     env->CallStaticVoidMethod(clazz, env->GetStaticMethodID(clazz, "errorCallbackFromNative","(I)V"), error);
+
+    if (needsDetach) {
+        AudioSystem_detachJNI();
+    }
 }
 
 static int
@@ -282,6 +312,12 @@
     return (jint) afLatency;
 }
 
+static jint
+android_media_AudioSystem_setLowRamDevice(JNIEnv *env, jobject clazz, jboolean isLowRamDevice)
+{
+    return (jint) AudioSystem::setLowRamDevice((bool) isLowRamDevice);
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
@@ -308,6 +344,7 @@
     {"getPrimaryOutputSamplingRate", "()I", (void *)android_media_AudioSystem_getPrimaryOutputSamplingRate},
     {"getPrimaryOutputFrameCount",   "()I", (void *)android_media_AudioSystem_getPrimaryOutputFrameCount},
     {"getOutputLatency",    "(I)I",     (void *)android_media_AudioSystem_getOutputLatency},
+    {"setLowRamDevice",     "(Z)I",     (void *)android_media_AudioSystem_setLowRamDevice},
 };
 
 int register_android_media_AudioSystem(JNIEnv *env)
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index 3ff9dda..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
@@ -597,11 +601,14 @@
                                                   jshortArray javaAudioData,
                                                   jint offsetInShorts, jint sizeInShorts,
                                                   jint javaAudioFormat) {
-    return (android_media_AudioTrack_native_write_byte(env, thiz,
+    jint written = android_media_AudioTrack_native_write_byte(env, thiz,
                                                  (jbyteArray) javaAudioData,
                                                  offsetInShorts*2, sizeInShorts*2,
-                                                 javaAudioFormat)
-            / 2);
+                                                 javaAudioFormat);
+    if (written > 0) {
+        written /= 2;
+    }
+    return written;
 }
 
 
diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp
index f2b69c6..b9ed28e 100644
--- a/core/jni/android_net_LocalSocketImpl.cpp
+++ b/core/jni/android_net_LocalSocketImpl.cpp
@@ -44,26 +44,6 @@
 static jclass class_FileDescriptor;
 static jmethodID method_CredentialsInit;
 
-/*
- * private native FileDescriptor
- * create_native(boolean stream)
- *               throws IOException;
- */
-static jobject
-socket_create (JNIEnv *env, jobject object, jboolean stream)
-{
-    int ret;
-
-    ret = socket(PF_LOCAL, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
-
-    if (ret < 0) {
-        jniThrowIOException(env, errno);
-        return NULL;
-    }
-
-    return jniCreateFileDescriptor(env,ret);
-}
-
 /* private native void connectLocal(FileDescriptor fd,
  * String name, int namespace) throws IOException
  */
@@ -445,32 +425,6 @@
 #endif
 }
 
-static void socket_close (JNIEnv *env, jobject object, jobject fileDescriptor)
-{
-    int fd;
-    int err;
-
-    if (fileDescriptor == NULL) {
-        jniThrowNullPointerException(env, NULL);
-        return;
-    }
-
-    fd = jniGetFDFromFileDescriptor(env, fileDescriptor);
-
-    if (env->ExceptionOccurred() != NULL) {
-        return;
-    }
-
-    do {
-        err = close(fd);
-    } while (err < 0 && errno == EINTR);
-
-    if (err < 0) {
-        jniThrowIOException(env, errno);
-        return;
-    }
-}
-
 /**
  * Processes ancillary data, handling only
  * SCM_RIGHTS. Creates appropriate objects and sets appropriate
@@ -909,7 +863,6 @@
      /* name, signature, funcPtr */
     {"getOption_native", "(Ljava/io/FileDescriptor;I)I", (void*)socket_getOption},
     {"setOption_native", "(Ljava/io/FileDescriptor;III)V", (void*)socket_setOption},
-    {"create_native", "(Z)Ljava/io/FileDescriptor;", (void*)socket_create},
     {"connectLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V",
                                                 (void*)socket_connect_local},
     {"bindLocal", "(Ljava/io/FileDescriptor;Ljava/lang/String;I)V", (void*)socket_bind_local},
@@ -918,7 +871,6 @@
     {"shutdown", "(Ljava/io/FileDescriptor;Z)V", (void*)socket_shutdown},
     {"available_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_available},
     {"pending_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_pending},
-    {"close_native", "(Ljava/io/FileDescriptor;)V", (void*) socket_close},
     {"read_native", "(Ljava/io/FileDescriptor;)I", (void*) socket_read},
     {"readba_native", "([BIILjava/io/FileDescriptor;)I", (void*) socket_readba},
     {"writeba_native", "([BIILjava/io/FileDescriptor;)V", (void*) socket_writeba},
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index faae11e..526159f 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "NetUtils"
 
 #include "jni.h"
+#include "JNIHelp.h"
 #include <utils/misc.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
@@ -239,6 +240,13 @@
     return env->NewStringUTF(::dhcp_get_errmsg());
 }
 
+static void android_net_utils_markSocket(JNIEnv *env, jobject thiz, jint socket, jint mark)
+{
+    if (setsockopt(socket, SOL_SOCKET, SO_MARK, &mark, sizeof(mark)) < 0) {
+        jniThrowException(env, "java/lang/IllegalStateException", "Error marking socket");
+    }
+}
+
 // ----------------------------------------------------------------------------
 
 /*
@@ -255,6 +263,7 @@
     { "stopDhcp", "(Ljava/lang/String;)Z",  (void *)android_net_utils_stopDhcp },
     { "releaseDhcpLease", "(Ljava/lang/String;)Z",  (void *)android_net_utils_releaseDhcpLease },
     { "getDhcpError", "()Ljava/lang/String;", (void*) android_net_utils_getDhcpError },
+    { "markSocket", "(II)V", (void*) android_net_utils_markSocket },
 };
 
 int register_android_net_NetworkUtils(JNIEnv* env)
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/jni/android_view_InputDevice.cpp b/core/jni/android_view_InputDevice.cpp
index 8ef5d0b..e5e3db0 100644
--- a/core/jni/android_view_InputDevice.cpp
+++ b/core/jni/android_view_InputDevice.cpp
@@ -57,7 +57,7 @@
             gInputDeviceClassInfo.ctor, deviceInfo.getId(), deviceInfo.getGeneration(),
             nameObj.get(), descriptorObj.get(), deviceInfo.isExternal(),
             deviceInfo.getSources(), deviceInfo.getKeyboardType(),
-            kcmObj.get(), deviceInfo.hasVibrator()));
+            kcmObj.get(), deviceInfo.hasVibrator(), deviceInfo.hasButtonUnderPad()));
 
     const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges();
     for (size_t i = 0; i < ranges.size(); i++) {
@@ -86,8 +86,8 @@
     FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
     gInputDeviceClassInfo.clazz = jclass(env->NewGlobalRef(gInputDeviceClassInfo.clazz));
 
-    GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz,
-            "<init>", "(IILjava/lang/String;Ljava/lang/String;ZIILandroid/view/KeyCharacterMap;Z)V");
+    GET_METHOD_ID(gInputDeviceClassInfo.ctor, gInputDeviceClassInfo.clazz, "<init>",
+            "(IILjava/lang/String;Ljava/lang/String;ZIILandroid/view/KeyCharacterMap;ZZ)V");
 
     GET_METHOD_ID(gInputDeviceClassInfo.addMotionRange, gInputDeviceClassInfo.clazz,
             "addMotionRange", "(IIFFFFF)V");
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 73c0683..ad6c0f2 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -18,6 +18,7 @@
 
 #include "JNIHelp.h"
 
+#include <SkMatrix.h>
 #include <android_runtime/AndroidRuntime.h>
 #include <utils/Log.h>
 #include <input/Input.h>
@@ -26,9 +27,6 @@
 #include "android_util_Binder.h"
 #include "android/graphics/Matrix.h"
 
-#include "SkMatrix.h"
-
-
 namespace android {
 
 // ----------------------------------------------------------------------------
@@ -680,7 +678,18 @@
         jint nativePtr, jobject matrixObj) {
     SkMatrix* matrix = android_graphics_Matrix_getSkMatrix(env, matrixObj);
     MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
-    event->transform(matrix);
+
+    float m[9];
+    m[0] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleX));
+    m[1] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewX));
+    m[2] = SkScalarToFloat(matrix->get(SkMatrix::kMTransX));
+    m[3] = SkScalarToFloat(matrix->get(SkMatrix::kMSkewY));
+    m[4] = SkScalarToFloat(matrix->get(SkMatrix::kMScaleY));
+    m[5] = SkScalarToFloat(matrix->get(SkMatrix::kMTransY));
+    m[6] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp0));
+    m[7] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp1));
+    m[8] = SkScalarToFloat(matrix->get(SkMatrix::kMPersp2));
+    event->transform(m);
 }
 
 static jint android_view_MotionEvent_nativeReadFromParcel(JNIEnv* env, jclass clazz,
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 842a7f7..bf6753d 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -107,7 +107,7 @@
         return NULL;
     }
 
-    sp<Surface> surface(new Surface(bufferProducer));
+    sp<Surface> surface(new Surface(bufferProducer, true));
     if (surface == NULL) {
         return NULL;
     }
@@ -143,7 +143,7 @@
     }
 
     sp<IGraphicBufferProducer> bq = st->getBufferQueue();
-    sp<Surface> surface(new Surface(bq));
+    sp<Surface> surface(new Surface(bq, true));
     if (surface == NULL) {
         jniThrowException(env, OutOfResourcesException, NULL);
         return 0;
@@ -319,7 +319,7 @@
     sp<IGraphicBufferProducer> gbp(interface_cast<IGraphicBufferProducer>(binder));
     if (gbp != NULL) {
         // we have a new IGraphicBufferProducer, create a new Surface for it
-        sur = new Surface(gbp);
+        sur = new Surface(gbp, true);
         // and keep a reference before passing to java
         sur->incStrong(&sRefBaseOwner);
     }
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 7ddb0dc..fb8359b 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1988,6 +1988,13 @@
         android:description="@string/permdesc_readFrameBuffer"
         android:protectionLevel="signature|system" />
 
+    <!-- Allows an application to use InputFlinger's low level features.
+    <p>Not for use by third-party applications. -->
+    <permission android:name="android.permission.ACCESS_INPUT_FLINGER"
+        android:label="@string/permlab_accessInputFlinger"
+        android:description="@string/permdesc_accessInputFlinger"
+        android:protectionLevel="signature" />
+
     <!-- Allows an application to configure and connect to Wifi displays
          @hide -->
     <permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY"
@@ -2259,6 +2266,16 @@
         android:description="@string/permdesc_modifyNetworkAccounting"
         android:protectionLevel="signature|system" />
 
+    <!-- Allows an application to mark traffic as from another user for per user routing.
+         Used by system wide services like media server that execute delegated network connections
+         for users.
+         @hide
+    -->
+    <permission android:name="android.permission.MARK_NETWORK_SOCKET"
+        android:label="@string/permlab_markNetworkSocket"
+        android:description="@string/permdesc_markNetworkSocket"
+        android:protectionLevel="signature|system" />
+
     <!-- C2DM permission.
          @hide Used internally.
      -->
@@ -2338,6 +2355,14 @@
         android:description="@string/permdesc_bindNotificationListenerService"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to call into a carrier setup flow. It is up to the
+         carrier setup application to enforce that this permission is required
+         @hide This is not a third-party API (intended for OEMs and system apps). -->
+    <permission android:name="android.permission.INVOKE_CARRIER_SETUP"
+        android:label="@string/permlab_invokeCarrierSetup"
+        android:description="@string/permdesc_invokeCarrierSetup"
+        android:protectionLevel="signature|system" />
+
     <!-- The system process is explicitly the only one allowed to launch the
          confirmation UI for full backup/restore -->
     <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/>
@@ -2488,6 +2513,12 @@
             </intent-filter>
         </receiver>
 
+        <receiver android:name="com.android.server.updates.CarrierProvisioningUrlsInstallReceiver" >
+            <intent-filter>
+                <action android:name="android.intent.action.UPDATE_CARRIER_PROVISIONING_URLS" />
+            </intent-filter>
+        </receiver>
+
         <receiver android:name="com.android.server.updates.TZInfoInstallReceiver" >
             <intent-filter>
                 <action android:name="android.intent.action.UPDATE_TZINFO" />
diff --git a/core/res/res/layout/list_menu_item_checkbox.xml b/core/res/res/layout/list_menu_item_checkbox.xml
index 02febc4..dc02a1e 100644
--- a/core/res/res/layout/list_menu_item_checkbox.xml
+++ b/core/res/res/layout/list_menu_item_checkbox.xml
@@ -4,9 +4,9 @@
      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.
@@ -18,8 +18,9 @@
     android:id="@+id/checkbox"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_alignParentEnd="true"
-    android:layout_centerVertical="true"
+    android:layout_gravity="center_vertical"
     android:focusable="false"
     android:clickable="false"
     android:duplicateParentState="true" />
+
+
diff --git a/core/res/res/layout/list_menu_item_icon.xml b/core/res/res/layout/list_menu_item_icon.xml
index 89b6b09..a30be6a 100644
--- a/core/res/res/layout/list_menu_item_icon.xml
+++ b/core/res/res/layout/list_menu_item_icon.xml
@@ -4,9 +4,9 @@
      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.
@@ -18,12 +18,11 @@
     android:id="@+id/icon"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_centerVertical="true"
-    android:layout_alignParentStart="true"
+    android:layout_gravity="center_vertical"
     android:layout_marginStart="8dip"
     android:layout_marginEnd="-8dip"
     android:layout_marginTop="8dip"
     android:layout_marginBottom="8dip"
     android:scaleType="centerInside"
-    android:duplicateParentState="true"
-    android:contentDescription="@null" />
+    android:duplicateParentState="true" />
+
diff --git a/core/res/res/layout/list_menu_item_layout.xml b/core/res/res/layout/list_menu_item_layout.xml
index 3130215..e8d4983 100644
--- a/core/res/res/layout/list_menu_item_layout.xml
+++ b/core/res/res/layout/list_menu_item_layout.xml
@@ -4,9 +4,9 @@
      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.
@@ -16,38 +16,46 @@
 
 <com.android.internal.view.menu.ListMenuItemView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
-    android:layout_height="?android:attr/listPreferredItemHeightSmall"
-    android:gravity="start|center_vertical">
-
+    android:layout_height="?android:attr/listPreferredItemHeightSmall">
+    
     <!-- Icon will be inserted here. -->
-
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="wrap_content"
+    
+    <!-- The title and summary have some gap between them, and this 'group' should be centered vertically. -->
+    <RelativeLayout
+        android:layout_width="0dip"
+        android:layout_weight="1"
         android:layout_height="wrap_content"
-        android:layout_alignWithParentIfMissing="true"
+        android:layout_gravity="center_vertical"
         android:layout_marginStart="?android:attr/listPreferredItemPaddingStart"
         android:layout_marginEnd="?android:attr/listPreferredItemPaddingEnd"
-        android:layout_toEndOf="@id/icon"
-        android:textAppearance="?android:attr/textAppearanceListItemSmall"
-        android:singleLine="true"
-        android:duplicateParentState="true"
-        android:ellipsize="marquee"
-        android:fadingEdge="horizontal"
-        android:textAlignment="viewStart" />
+        android:duplicateParentState="true">
+        
+        <TextView 
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentStart="true"
+            android:textAppearance="?android:attr/textAppearanceListItemSmall"
+            android:singleLine="true"
+            android:duplicateParentState="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:textAlignment="viewStart" />
 
-    <TextView
-        android:id="@+id/shortcut"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/title"
-        android:layout_alignStart="@id/title"
-        android:layout_alignWithParentIfMissing="true"
-        android:textAppearance="?android:attr/textAppearanceSmall"
-        android:singleLine="true"
-        android:duplicateParentState="true"
-        android:textAlignment="viewStart" />
+        <TextView
+            android:id="@+id/shortcut"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/title"
+            android:layout_alignParentStart="true"
+            android:textAppearance="?android:attr/textAppearanceSmall"
+            android:singleLine="true"
+            android:duplicateParentState="true"
+            android:textAlignment="viewStart" />
+
+    </RelativeLayout>
 
     <!-- Checkbox, and/or radio button will be inserted here. -->
-
+    
 </com.android.internal.view.menu.ListMenuItemView>
diff --git a/core/res/res/layout/list_menu_item_radio.xml b/core/res/res/layout/list_menu_item_radio.xml
index 74564ad..ac4459e 100644
--- a/core/res/res/layout/list_menu_item_radio.xml
+++ b/core/res/res/layout/list_menu_item_radio.xml
@@ -4,9 +4,9 @@
      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.
@@ -18,8 +18,7 @@
     android:id="@+id/radio"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
-    android:layout_alignParentEnd="true"
-    android:layout_centerVertical="true"
+    android:layout_gravity="center_vertical"
     android:focusable="false"
     android:clickable="false"
     android:duplicateParentState="true" />
diff --git a/core/res/res/layout/popup_menu_item_layout.xml b/core/res/res/layout/popup_menu_item_layout.xml
index aff3779..452f85d 100644
--- a/core/res/res/layout/popup_menu_item_layout.xml
+++ b/core/res/res/layout/popup_menu_item_layout.xml
@@ -4,9 +4,9 @@
      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.
@@ -18,37 +18,45 @@
     android:layout_width="match_parent"
     android:layout_height="?android:attr/dropdownListPreferredItemHeight"
     android:minWidth="196dip"
-    android:paddingEnd="16dip"
-    android:gravity="start|center_vertical">
-
+    android:paddingEnd="16dip">
+    
     <!-- Icon will be inserted here. -->
-
-    <TextView
-        android:id="@+id/title"
-        android:layout_width="wrap_content"
+    
+    <!-- The title and summary have some gap between them, and this 'group' should be centered vertically. -->
+    <RelativeLayout
+        android:layout_width="0dip"
+        android:layout_weight="1"
         android:layout_height="wrap_content"
-        android:layout_alignWithParentIfMissing="true"
+        android:layout_gravity="center_vertical"
         android:layout_marginStart="16dip"
-        android:layout_toEndOf="@id/icon"
-        android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
-        android:singleLine="true"
-        android:duplicateParentState="true"
-        android:ellipsize="marquee"
-        android:fadingEdge="horizontal"
-        android:textAlignment="viewStart" />
+        android:duplicateParentState="true">
+        
+        <TextView 
+            android:id="@+id/title"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentStart="true"
+            android:textAppearance="?android:attr/textAppearanceLargePopupMenu"
+            android:singleLine="true"
+            android:duplicateParentState="true"
+            android:ellipsize="marquee"
+            android:fadingEdge="horizontal"
+            android:textAlignment="viewStart" />
 
-    <TextView
-        android:id="@+id/shortcut"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/title"
-        android:layout_alignStart="@id/title"
-        android:layout_alignWithParentIfMissing="true"
-        android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
-        android:singleLine="true"
-        android:duplicateParentState="true"
-        android:textAlignment="viewStart" />
+        <TextView
+            android:id="@+id/shortcut"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_below="@id/title"
+            android:layout_alignParentStart="true"
+            android:textAppearance="?android:attr/textAppearanceSmallPopupMenu"
+            android:singleLine="true"
+            android:duplicateParentState="true"
+            android:textAlignment="viewStart" />
+
+    </RelativeLayout>
 
     <!-- Checkbox, and/or radio button will be inserted here. -->
-
+    
 </com.android.internal.view.menu.ListMenuItemView>
diff --git a/core/res/res/layout/pin_challenge.xml b/core/res/res/layout/restrictions_pin_challenge.xml
similarity index 65%
copy from core/res/res/layout/pin_challenge.xml
copy to core/res/res/layout/restrictions_pin_challenge.xml
index 2cb14b4..954af92 100644
--- a/core/res/res/layout/pin_challenge.xml
+++ b/core/res/res/layout/restrictions_pin_challenge.xml
@@ -37,38 +37,13 @@
             android:text="@string/restr_pin_create_pin"
             android:textColor="?android:attr/textColorSecondary" />
 
-        <!-- TextView android:id="@+id/pin1_label"
-            style="?android:attr/textAppearanceSmall"
-            android:layout_marginBottom="16dp"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/restr_pin_enter_pin"
-            android:textColor="?android:attr/textColorSecondary" /-->
-
-        <EditText android:id="@+id/pin1_text"
+        <EditText android:id="@+id/pin_text"
             style="?android:attr/textAppearanceMedium"
             android:layout_marginBottom="16dp"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:hint="@string/restr_pin_enter_pin"
-            android:inputType="textPassword"
-            android:textColor="?android:attr/textColorPrimary" />
-
-        <!-- TextView android:id="@+id/pin2_label"
-            style="?android:attr/textAppearanceSmall"
-            android:layout_marginBottom="16dp"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/restr_pin_confirm_pin"
-            android:textColor="?android:attr/textColorSecondary" /-->
-
-        <EditText android:id="@+id/pin2_text"
-            style="?android:attr/textAppearanceMedium"
-            android:layout_marginBottom="16dp"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:hint="@string/restr_pin_confirm_pin"
-            android:inputType="textPassword"
+            android:inputType="numberPassword"
             android:textColor="?android:attr/textColorPrimary" />
 
         <TextView android:id="@+id/pin_error_message"
diff --git a/core/res/res/layout/pin_challenge.xml b/core/res/res/layout/restrictions_pin_setup.xml
similarity index 76%
rename from core/res/res/layout/pin_challenge.xml
rename to core/res/res/layout/restrictions_pin_setup.xml
index 2cb14b4..03ed696 100644
--- a/core/res/res/layout/pin_challenge.xml
+++ b/core/res/res/layout/restrictions_pin_setup.xml
@@ -37,38 +37,31 @@
             android:text="@string/restr_pin_create_pin"
             android:textColor="?android:attr/textColorSecondary" />
 
-        <!-- TextView android:id="@+id/pin1_label"
-            style="?android:attr/textAppearanceSmall"
-            android:layout_marginBottom="16dp"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/restr_pin_enter_pin"
-            android:textColor="?android:attr/textColorSecondary" /-->
-
-        <EditText android:id="@+id/pin1_text"
+        <EditText android:id="@+id/pin_text"
             style="?android:attr/textAppearanceMedium"
             android:layout_marginBottom="16dp"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:hint="@string/restr_pin_enter_pin"
-            android:inputType="textPassword"
+            android:hint="@string/restr_pin_enter_old_pin"
+            android:inputType="numberPassword"
             android:textColor="?android:attr/textColorPrimary" />
 
-        <!-- TextView android:id="@+id/pin2_label"
-            style="?android:attr/textAppearanceSmall"
+        <EditText android:id="@+id/pin_new_text"
+            style="?android:attr/textAppearanceMedium"
             android:layout_marginBottom="16dp"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:text="@string/restr_pin_confirm_pin"
-            android:textColor="?android:attr/textColorSecondary" /-->
+            android:hint="@string/restr_pin_enter_new_pin"
+            android:inputType="numberPassword"
+            android:textColor="?android:attr/textColorPrimary" />
 
-        <EditText android:id="@+id/pin2_text"
+        <EditText android:id="@+id/pin_confirm_text"
             style="?android:attr/textAppearanceMedium"
             android:layout_marginBottom="16dp"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:hint="@string/restr_pin_confirm_pin"
-            android:inputType="textPassword"
+            android:inputType="numberPassword"
             android:textColor="?android:attr/textColorPrimary" />
 
         <TextView android:id="@+id/pin_error_message"
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 06a5e67..3d6d9da 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Laat die program toe om SurfaceFlinger se laevlak-kenmerke te gebruik."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lees raambuffer"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Laat die program toe om die inhoud van die raambuffer te lees."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"stel Wi-Fi-skerms op"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Laat die program toe om Wi-Fi-skerms op te stel en daaraan te koppel."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"beheer Wi-Fi-skerms"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Laat die houer toe om aan die top-koppelvlak van \'n kennisgewingluisteraardiens te bind. Behoort nooit vir gewone programme nodig te wees nie."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Stel wagwoordreëls"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Beheer lengte en watter karakters wat in die skermontsluit-wagwoorde gebruik word."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Monitor pogings om skerm te ontsluit"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 8c0da87..e719f72 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"መተግበሪያውን የSurfaceFlinger ዝቅተኛ ደረጃ ባህሪያትን ለመጠቀም ይፈቅዳል።"</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"የንዑስ ክፈፍ ቋት አንብብ"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"የክፈፍ ቋት ይዘት ለማንበብ ለመተግበሪያው ይፈቅዳሉ።"</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"የWifi ማሳያዎችን አዋቅር"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"መተግበሪያው የWifi ማሳያዎችን እንዲያዋቅርና ከእነሱ ጋር እንዲገናኝ ይፈቅድለታል።"</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"የWifi ማሳያዎችን ተቆጣጠር"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"ያዢው የማሳወቂያ አዳማጭ አገልግሎቱን ከከፍተኛ-ደረጃ በይነገጹ ጋር እንዲያስር ያስችለዋል። ለመደበኛ መተግበሪያዎች በጭራሽ አያስፈልግም።"</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"የይለፍ ቃል ድንቦች አዘጋጅ"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"በማያ-መክፈት የተፈቀዱ የይለፍ ቃል ርዝመት እና ቁምፊዎች ተቆጣጠር።"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"የማሳያ-ክፈት ሙከራዎችን አሳይ"</string>
@@ -941,8 +953,8 @@
     <string name="menu_space_shortcut_label" msgid="2410328639272162537">"ቦታ"</string>
     <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"አሰገባ"</string>
     <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"ሰርዝ"</string>
-    <string name="search_go" msgid="8298016669822141719">" ፈልግ"</string>
-    <string name="searchview_description_search" msgid="6749826639098512120">"ፈልግ"</string>
+    <string name="search_go" msgid="8298016669822141719">"ፍለጋ"</string>
+    <string name="searchview_description_search" msgid="6749826639098512120">"ፍለጋ"</string>
     <string name="searchview_description_query" msgid="5911778593125355124">"ጥያቄ ፍለጋ"</string>
     <string name="searchview_description_clear" msgid="1330281990951833033">"ጥያቄ አጥራ"</string>
     <string name="searchview_description_submit" msgid="2688450133297983542">"ጥያቄ አስረክብ"</string>
@@ -1394,7 +1406,7 @@
     <string name="description_target_camera" msgid="969071997552486814">"ካሜራ"</string>
     <string name="description_target_silent" msgid="893551287746522182">"ፀጥታ"</string>
     <string name="description_target_soundon" msgid="30052466675500172">"ድምፅ አብራ"</string>
-    <string name="description_target_search" msgid="3091587249776033139">"ፈልግ"</string>
+    <string name="description_target_search" msgid="3091587249776033139">"ፍለጋ"</string>
     <string name="description_target_unlock_tablet" msgid="3833195335629795055">"ላለመቆለፍ አንሸራት፡፡"</string>
     <string name="keyboard_headset_required_to_hear_password" msgid="7011927352267668657">"የይለፍ ቃል ቁልፎች  ሲነገሩ ለመስማት የጆሮ ማዳመጫ ሰካ።"</string>
     <string name="keyboard_password_character_no_headset" msgid="2859873770886153678">"ነጥብ."</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 1820689..88c55c5 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"للسماح للتطبيق باستخدام ميزات SurfaceFlinger ذات المستوى المنخفض."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"قراءة المخزن المؤقت للإطارات"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"للسماح للتطبيق بقراءة محتوى المخزن المؤقت للإطارات."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"تهيئة شاشات Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"للسماح للتطبيق بتهيئة شاشات Wi-Fi والاتصال بها."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"التحكم في شاشات Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"يتيح للمالك الربط بواجهة المستوى العلوي لخدمة تلقّي الإشعارات الصوتية. ولن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"تعيين قواعد كلمة المرور"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"يمكنك التحكم في الطول والأحرف المسموح بها في كلمات مرور إلغاء تأمين الشاشة."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"مراقبة محاولات إلغاء قفل الشاشة"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml
index becac6c..d6997f0 100644
--- a/core/res/res/values-be/strings.xml
+++ b/core/res/res/values-be/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Дазваляе прыкладанням выкарыстоўваць нізкаўзроўневыя функцыі SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"чытаць буфер кадраў"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Дазваляе прыкладанням счытваць змесціва буферу кадра."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"налада дысплеяў Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Дазволiць прыкладанню наладжвацца i падключацца да дысплеяў Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"кіраванне дысплеямi Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Дазваляе ўладальніку прывязвацца да верхняга ўзроўню інтэрфейсу службы  апавяшчэння слухачоў. Ніколі не патрэбнае для звычайных прыкладанняў."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Устанавіць правілы паролю"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Кіраванне даўжынёй і колькасцю знакаў у паролі разблакоўкі экрана."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Сачыць за спробамі разблакоўкі экрана"</string>
@@ -1594,6 +1606,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index a4fa7f0..8d0826c 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Разрешава на приложението да използва функциите на SurfaceFlinger от ниско ниво."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"четене на кадровия буфер"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Разрешава на приложението да чете съдържанието на кадровия буфер."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"конфигуриране на дисплеите през WiFi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Разрешава на приложението да конфигурира и да се свързва с дисплеите през WiFi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"контролиране на дисплеите през WiFi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Разрешава на притежателя да се обвърже с интерфейса от първо ниво на услуга за слушател на известия. Нормалните приложения не би трябвало никога да се нуждаят от това."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Задаване на правила за паролата"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролирайте дължината и разрешените знаци за паролите за отключване на екрана."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Наблюдаване на опитите за отключване на екрана"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 05965b3..bc7b539 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permet que l\'aplicació utilitzi funcions SurfaceFlinger de baix nivell."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"llegir la memòria intermèdia de marcs"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permet que l\'aplicació llegeixi el contingut de la memòria intermèdia de marcs."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configuració de les pantalles Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permet a l\'aplicació configurar-se i connectar-se a les pantalles Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"control de les pantalles Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permet que el titular vinculi la interfície de nivell superior d\'un servei de processament de notificacions. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Defineix les normes de contrasenya"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controla la longitud i els caràcters permesos a les contrasenyes de desbloqueig de pantalla."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Control d\'intents de desbloqueig de pantalla"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index 9f3a090..7805977 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Umožňuje aplikaci používat nízkoúrovňové funkce SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"čtení vyrovnávací paměti snímků"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Umožňuje aplikaci číst obsah vyrovnávací paměti snímků."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurovat displeje přes Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Povoluje aplikaci připojit a konfigurovat displeje přes Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"ovládat displeje přes Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Umožňuje držiteli navázat se na nejvyšší úroveň služby pro poslouchání oznámení. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Nastavit pravidla pro heslo"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Řídit délku hesel pro odemčení obrazovky a povolené znaky."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Sledovat pokusy o odemčení obrazovky"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 80e14c8..5bb2ae8 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Tillader, at appen kan bruge SurfaceFlinger-funktioner på lavt niveau."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"læs rammebuffer"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Tillader, at appen kan læse indholdet fra rammebufferen."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurer Wi-Fi-skærme"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Tillader, at appen konfigurerer og opretter forbindelse til Wi-Fi-skærme."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"kontrollér Wi-Fi-skærme"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Tillader brugeren at forpligte sig til en underretningslyttertjenestes grænseflade på øverste niveau. Bør aldrig være nødvendigt til almindelige apps."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Indstil regler for adgangskode"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontroller længden samt tilladte tegn i adgangskoder til oplåsning af skærmen."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Overvåg forsøg på oplåsning af skærm"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index be81e63..7716e84 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Ermöglicht der App, die systemnahen SurfaceFlinger-Funktionen zu verwenden"</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"Frame-Puffer lesen"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Ermöglicht der App, den Inhalt des Frame-Puffers zu lesen"</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"WLAN-Anzeigen konfigurieren"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Erlaubt der App, WLAN-Anzeigen zu konfigurieren und eine Verbindung zu diesen herzustellen"</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"WLAN-Anzeigen steuern"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ermöglicht dem Inhaber, sich an die Oberfläche der obersten Ebene eines Benachrichtigungs-Listener-Dienstes zu binden. Sollte nie für normale Apps benötigt werden."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Passwortregeln festlegen"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Zulässige Länge und Zeichen für Passwörter zum Entsperren des Bildschirms festlegen"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Versuche zum Entsperren des Displays überwachen"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 961e15e..fecbaaa 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Επιτρέπει σε μια εφαρμογή να χρησιμοποιεί λειτουργίες SurfaceFlinger χαμηλού επιπέδου."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"ανάγνωση προσωρινής μνήμης πλαισίου"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Επιτρέπει στην εφαρμογή την ανάγνωση του περιεχομένου της προσωρινής μνήμης πλαισίου."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"διαμόρφωση οθονών Wifi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Επιτρέπει τη διαμόρφωση της εφαρμογής και τη σύνδεσης σε οθόνες Wifi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"έλεγχος οθονών Wifi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Επιτρέπει στον κάτοχο τη δέσμευση στη διεπαφή ανωτάτου επιπέδου μιας υπηρεσίας ακρόασης ειδοποιήσεων. Δεν απαιτείται σε κανονικές εφαρμογές."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Ορισμός κανόνων κωδικού πρόσβασης"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Έλεγχος του μεγέθους και των χαρακτήρων που επιτρέπονται στους κωδικούς πρόσβασης ξεκλειδώματος οθόνης."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Παρακολούθηση προσπαθειών ξεκλειδώματος οθόνης"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index 4825085..63d5f66 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Allows the app to use SurfaceFlinger low-level features."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"read frame buffer"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Allows the app to read the content of the frame buffer."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configure Wi-Fi displays"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Allows the app to configure and connect to Wi-Fi displays."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"control Wi-Fi displays"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Set password rules"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Control the length and the characters allowed in screen-unlock passwords."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Monitor screen-unlock attempts"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index 615fcbe..1eafed5 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permite que la aplicación utilice funciones de SurfaceFlinger de bajo nivel."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"leer el búfer de tramas"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permite que la aplicación lea el contenido del búfer de tramas."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurar pantallas Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permite que la aplicación configure y se conecte a pantallas Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controlar pantallas Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite al propietario vincularse a la interfaz de nivel superior de un servicio de agente de escucha de notificaciones. Las aplicaciones normales no deberían necesitar este permiso."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Establecer reglas de contraseña"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar la longitud y los caracteres permitidos en las contraseñas para desbloquear la pantalla"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Supervisa los intentos para desbloquear la pantalla"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 2dbe795..dd4bac9 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permite que la aplicación use funciones de SurfaceFlinger de nivel inferior."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"leer memoria de almacenamiento intermedio"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permite que la aplicación lea el contenido de la memoria de almacenamiento intermedio."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurar pantallas Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permite que la aplicación configure pantallas Wi-Fi y se conecte a ellas."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controlar pantallas Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite enlazar con la interfaz de nivel superior de un servicio de detector de notificaciones. No debe ser necesario para las aplicaciones normales."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Establecimiento de reglas de contraseña"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar la longitud y los caracteres permitidos en las contraseñas de bloqueo de pantalla"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Control de intentos de bloqueo de pantalla"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml
index cd3d97c..d5a8eed 100644
--- a/core/res/res/values-et/strings.xml
+++ b/core/res/res/values-et/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Võimaldab rakendusel kasutada SurfaceFlingeri madalatasemelisi funktsioone."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"loe kaadripuhvrit"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Võimaldab rakendusel kaadripuhvri sisu lugeda."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"WiFi-ekraanide seadistamine"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Lubab rakendusel seadistada WiFi-ekraane ja nendega ühendus luua."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"WiFi-ekraanide juhtimine"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Võimaldab omanikul siduda märguannete kuulamisteenuse ülemise taseme kasutajaliidese. Seda ei tohiks tavarakenduste puhul kunagi vaja olla."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Parooli reeglite määramine"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrollige ekraaniluku avamise paroolide pikkust ja tähemärke."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Ekraani avamiskatsed"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index d4b1cb7..6043d64 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"اجازه می‎دهد برنامه از ویژگی‌های سطح پایین SurfaceFlinger استفاده کند."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"خواندن بافر قاب"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"به برنامه اجازه می‎دهد تا محتوای بافر کادر را بخواند."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"پیکربندی صفحه نمایش‌های Wi‑Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"به برنامه اجازه می‌دهد تا اتصال به صفحات نمایش Wi‑Fi را پیکربندی کند."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"کنترل صفحه نمایش‌های Wi‑Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"به دارنده اجازه می‌دهد به یک رابط سطح بالای سرویس شنونده اعلان متصل شود. هرگز نباید برای برنامه‌های عادی لازم شود."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"تنظیم قوانین رمز ورود"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"طول و نویسه‎های مجاز در گذرواژه‌های بازکردن قفل صفحه را کنترل کنید."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"نمایش تلاش‌های قفل گشایی صفحه"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index bded5cc..744d818 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Antaa sovelluksen käyttää SurfaceFlingerin matalan tason ominaisuuksia."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lue kehyspuskuria"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Antaa sovelluksen lukea kehyspuskurin sisältöä."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"määritä wifi-näyttöjen asetukset"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Antaa sovelluksen määrittää wifi-näyttöjä ja muodostaa yhteyden niihin."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"hallitse wifi-näyttöjä"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Antaa sovelluksen sitoutua ilmoituskuuntelijan ylimmän tason käyttöliittymään. Ei tavallisten sovelluksien käyttöön."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Aseta salasanasäännöt"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Hallinnoi ruudun lukituksenpoistosalasanoissa sallittuja merkkejä ja salasanan pituutta."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Tarkkaile ruudun lukituksen poistoyrityksiä"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 549c4ae..8e75805 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permet à l\'application d\'utiliser les fonctionnalités de bas niveau de SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"Lecture de la mémoire tampon graphique"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permet à l\'application de lire le contenu de la mémoire tampon graphique."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurer les écrans Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permet à l\'application de configurer des écrans Wi-Fi et de s\'y connecter."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"contrôler les écrans Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permet à l\'application de s\'associer à l\'interface de niveau supérieur d\'un service d\'écoute des notifications. Ne devrait jamais être nécessaire pour les applications normales."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Définir les règles du mot de passe"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Choisir le nombre et le type de caractères autorisés dans les mots de passe de déverrouillage de l\'écran"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Gérer les tentatives de déverrouillage de l\'écran"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 768dbd1..8c63d92 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"एप्‍लिकेशन को SurfaceFlinger निम्‍न-स्‍तर सुविधाएं उपयोग करने देता है."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"फ़्रेम बफ़र पढ़ें"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"एप्‍लिकेशन को फ़्रेम बफ़र की सामग्री पढ़ने देता है."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Wifi डिस्प्ले को कॉन्फ़िगर करें"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"एप्लिकेशन को कॉन्फ़िगर करने देता है और Wifi डिस्प्ले से कनेक्ट करता है."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Wifi डिस्प्ले को नियंत्रित करें"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"धारक को सूचना श्रवणकर्ता सेवा के शीर्ष स्तरीय इंटरफ़ेस से जुड़ने देती है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होनी चाहिए."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"पासवर्ड नियम सेट करें"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"स्‍क्रीन-अनलॉक पासवर्ड में अनुमति प्राप्त लंबाई और वर्णों को नियंत्रित करें."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"स्‍क्रीन-अनलॉक के प्रयासों पर निगरानी रखें"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 730fb21..0331bd8 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Aplikaciji omogućuje upotrebu značajki niske razine SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"čitanje međuspremnika okvira"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Aplikaciji omogućuje čitanje sadržaja međuspremnika okvira."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfiguriraj Wifi zaslone"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Omogućuje aplikaciji konfiguriranje i povezivanje s Wi-Fi zaslonima."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"upravljaj Wifi zaslonima"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Nositelju omogućuje vezanje uz sučelje najviše razine usluge slušatelja obavijesti. Ne bi smjelo biti potrebno za uobičajene aplikacije."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Postavi pravila zaporke"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Upravljajte duljinom zaporki za otključavanje zaslona i dopuštenim znakovima u tim zaporkama."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Nadgledaj pokušaje otključavanja zaslona"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 23f8e74..0e3359b 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Lehetővé teszi az alkalmazás számára a SurfaceFlinger alacsony szintű funkciók használatát."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"keretpuffer olvasása"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Lehetővé teszi az alkalmazás számára a keretpuffer tartalmának olvasását."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Wi-Fi kijelzők konfigurálása"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Lehetővé teszi, hogy az alkalmazás Wi-Fi kijelzőket konfiguráljon, és csatlakozzon hozzájuk."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Wi-Fi kijelzők irányítása"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lehetővé teszi a használó számára, hogy csatlakozzon egy értesítésfigyelő szolgáltatás legfelső szintű felületéhez. A normál alkalmazásoknak erre soha nincs szükségük."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Jelszavakkal kapcsolatos szabályok beállítása"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"A képernyőzár-feloldási jelszavakban engedélyezett karakterek és hosszúság vezérlése."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Képernyőzár-feloldási kísérletek figyelése"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index ee652c5..c01df57 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Mengizinkan apl menggunakan fitur tingkat rendah SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"baca buffer frame"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Mengizinkan apl membaca konten penyangga frame."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"mengonfigurasi tampilan Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Izinkan aplikasi mengonfigurasi dan terhubung ke tampilan Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"mengontrol tampilan Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Memungkinkan pemegang mengikat antarmuka tingkat teratas dari suatu layanan pendengar pemberitahuan. Tidak pernah diperlukan oleh aplikasi normal."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Setel aturan sandi"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrol panjang dan karakter yang diizinkan dalam sandi pembuka layar."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Upaya pembukaan kunci layar monitor"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 4059ad5..b877501 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Consente all\'applicazione l\'utilizzo di funzioni di basso livello SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lettura buffer di frame"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Consente all\'applicazione di leggere i contenuti del buffer di frame."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurazione di schermi Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Consente all\'applicazione di configurare schermi Wi-Fi e di collegarsi a essi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controllo di schermi Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Consente al titolare di vincolarsi all\'interfaccia di primo livello di un servizio listener di notifica. Non dovrebbe mai essere necessaria per le normali applicazioni."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Imposta regole password"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlla la lunghezza e i caratteri ammessi nelle password di sblocco dello schermo."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Monitora tentativi di sblocco dello schermo"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 6168fdf..11ff720 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"מאפשר ליישום להשתמש בתכונות ברמה הנמוכה של SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"אחסון זמני של מסגרת קריאה"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"מאפשר ליישום לקרוא את התוכן של מאגר הנתונים הזמני של המסגרות."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"הגדר תצוגות Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"מאפשר לאפליקציה להגדיר ולהתחבר לתצוגות Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"שלוט בתצוגות Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"הרשאה זו מאפשרת למשתמש לבצע איגוד לממשק הרמה העליונה של שירות מאזין להתראות. הרשאה זו אף פעם אינה נחוצה ליישומים רגילים."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"הגדר כללי סיסמה"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"שלוט באורך ובתווים המותרים בסיסמאות לביטול נעילת מסך."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"עקוב אחר ניסיונות לביטול נעילת מסך"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index eb1556d..41c0021 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"SurfaceFlingerの低レベルの機能の使用をアプリに許可します。"</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"フレームバッファの読み取り"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"フレームバッファの内容の読み取りをアプリに許可します。"</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Wi-Fiディスプレイの設定"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Wi-Fiディスプレイを設定して接続することをアプリに許可します。"</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Wi-Fiディスプレイの制御"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"通知リスナーサービスのトップレベルインターフェースにバインドすることを所有者に許可します。通常のアプリでは不要です。"</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"パスワードルールの設定"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"画面ロック解除パスワードの長さと使用できる文字を制御します。"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"画面ロック解除試行の監視"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 8cd152d..f079520 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"앱이 SurfaceFlinger의 하위 수준 기능을 사용할 수 있도록 허용합니다."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"프레임 버퍼 읽기"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"앱이 프레임 버퍼의 내용을 읽을 수 있도록 허용합니다."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Wi-Fi 디스플레이 설정"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"앱이 Wi-Fi 디스플레이를 설정하고 연결하도록 허용합니다."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Wi-Fi 디스플레이 제어"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"권한을 가진 프로그램이 알림 수신기 서비스에 대한 최상위 인터페이스를 사용하도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"비밀번호 규칙 설정"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"화면 잠금해제 비밀번호에 허용되는 길이 및 문자 수를 제어합니다."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"화면 잠금해제 시도 모니터링"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index ea10f2f..14d1af6 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Leidžiama programai naudoti „SurfaceFlinger“ žemo lygio funkcijas."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"skaityti kadrų buferį"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Leidžiama programai skaityti rėmelio buferio turinį."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigūruoti „Wi-Fi“ pateiktis"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Leidžiama programai konfigūruoti ir prisijungti prie „Wi-Fi“ pateikčių."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"valdyti „Wi-Fi“ pateiktis"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Leidžiama turėtojui susisaistyti su pranešimų skaitymo priemonės paslaugos aukščiausio lygio sąsaja. Įprastoms programoms to neturėtų prireikti."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Nustatyti slaptažodžio taisykles"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Valdyti leidžiamą ekrano atrakinimo slaptažodžių ilgį ir leidžiamus naudoti simbolius."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Stebėti bandymus atrakinti ekraną"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 3e3ab2c..7da9a73 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Ļauj lietotnei lietot SurfaceFlinger zema līmeņa funkcijas."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lasīt kadru buferi"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Ļauj lietotnei lasīt kadru bufera saturu."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Wi-Fi displeju konfigurēšana"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Ļauj lietotnei konfigurēt Wi-Fi displejus un veidot savienojumu ar tiem."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Wi-Fi displeju vadība"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ļauj īpašniekam izveidot saiti ar paziņojumu uztvērēja pakalpojuma augšējā līmeņa saskarni. Parastajām lietotnēm tas nekad nav nepieciešams."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Paroles kārtulu iestatīšana"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolē ekrāna atbloķēšanas parolē atļautās rakstzīmes un garumu."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Ekrāna atbloķēšanas mēģinājumu pārraudzīšana"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index 9260061..e72085e 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Membenarkan apl menggunakan ciri peringkat rendah SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"baca penimbal bingkai"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Membenarkan apl membaca kandungan penimbal bingkai."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurasikan paparan Wifi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Membenarkan apl mengkonfigurasi dan menyambung ke paparan Wifi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"kawal paparan Wifi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan pendengar pemberitahuan. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Tetapkan peraturan kata laluan"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Mengawal panjang dan aksara yang dibenarkan dalam kata laluan buka kunci skrin."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Memantau percubaan buka kunci skrin"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index b998377..60a7bef 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Lar appen bruke grunnleggende SurfaceFlinger-funksjoner."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"lese skjermbufferet"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Lar appen lese innholdet i rammebufferen."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurere Wi-Fi-skjermer"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Tillater appen å konfigurere og koble til Wi-Fi-skjermer."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"kontrollere Wi-Fi-skjermer"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lar innehaveren binde seg til det øverste grensesnittnivået for en varsellyttertjeneste. Skal aldri være nødvendig for vanlige apper."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Angi passordregler"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontroller tillatt lengde og tillatte tegn i passord for opplåsing av skjerm."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Overvåk forsøk på opplåsing av skjerm"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index af72700..1584122 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Hiermee kan de app SurfaceFlinger-functies op laag niveau gebruiken."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"framebuffer lezen"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Hiermee kan de app de inhoud van de framebuffer lezen."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"wifi-displays configureren"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"De app toestaan wifi-displays te configureren en hiermee verbinding te maken."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"wifi-displays beheren"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Hiermee kan de houder koppelen aan de hoofdinterface van een listener-service voor meldingen. Nooit vereist voor normale apps."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Wachtwoordregels instellen"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"De lengte en tekens beheren die zijn toegestaan in wachtwoorden voor schermontgrendeling."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Pogingen voor schermontgrendeling bijhouden"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index 588df6f..a56fdca 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Pozwala aplikacji na wykorzystanie funkcji niskiego poziomu usługi SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"czytanie bufora ramki"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Pozwala aplikacji na odczyt zawartości bufora ramki."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurowanie wyświetlaczy Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Zezwala aplikacji na konfigurację wyświetlaczy Wi-Fi i łączenie z nimi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"zarządzanie wyświetlaczami Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Zezwala na tworzenie powiązania z interfejsem najwyższego poziomu usługi odbiornika powiadomień. Nie powinno być nigdy potrzebne dla zwykłych aplikacji."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Określ reguły hasła"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolowanie długości haseł odblokowania ekranu i dozwolonych w nich znaków"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Monitoruj próby odblokowania ekranu"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 4d22c52..61463d0 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permite à aplicação utilizar funcionalidades de SurfaceFlinger de nível inferior."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"ler memória intermédia de fotogramas"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permite à aplicação ler o conteúdo da memória intermédia de fotogramas."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurar visores Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permite que a aplicação se configure e se ligue a visores Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controlar visores Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite que o titular vincule a interface de nível superior de um serviço de escuta de notificações. Nunca deverá ser necessário para aplicações normais."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Definir regras de palavra-passe"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controlar o comprimento e os caracteres permitidos nas palavras-passe de desbloqueio do ecrã."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Monitorizar tentativas de desbloqueio do ecrã"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index d9a997e..70392c0 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permite que o aplicativo use recursos com baixos níveis de SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"ler o buffer do frame"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permite que o aplicativo leia o conteúdo do buffer de frame."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurar monitores Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permite que o aplicativo configure e conecte a monitores Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controlar monitores Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite que o proprietário sujeite a interface de nível superior a um serviço ouvinte de notificações. Não deve ser necessário para aplicativos comuns."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Definir regras para senha"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Controle o tamanho e os caracteres permitidos nas senhas de desbloqueio de tela."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Monitorar tentativas de desbloqueio da tela"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index c5b4fd1..06d0afb 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -757,6 +757,10 @@
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"leger il paraculp da frame"</string>
     <!-- no translation found for permdesc_readFrameBuffer (4937405521809454680) -->
     <skip />
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <!-- no translation found for permlab_configureWifiDisplay (5595661694746742168) -->
     <skip />
     <!-- no translation found for permdesc_configureWifiDisplay (7916815158690218065) -->
@@ -1064,6 +1068,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) -->
@@ -1072,6 +1080,10 @@
     <skip />
     <!-- no translation found for permdesc_bindNotificationListenerService (985697918576902986) -->
     <skip />
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <!-- no translation found for policylab_limitPassword (4497420728857585791) -->
     <skip />
     <!-- no translation found for policydesc_limitPassword (3252114203919510394) -->
@@ -2534,6 +2546,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index a058815..96c6f47 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Permite aplicaţiei să utilizeze funcţiile de nivel redus SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"citire zonă tampon de cadre"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Permite aplicaţiei să citească conţinutul zonei-tampon a cadrului."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"configurează afişaje Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Permite aplicaţiei să configureze şi să se conecteze la afişaje Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"controlează afişaje Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Permite proprietarului să se conecteze la interfața de nivel superior a unui serviciu de citire a notificărilor. În mod normal aplicațiile nu ar trebui să aibă nevoie de această permisiune."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Setaţi reguli pentru parolă"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Stabiliţi lungimea şi tipul de caractere permise în parolele pentru deblocarea ecranului."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Monitorizaţi încercările de deblocare a ecranului"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index b742263..75d2e68 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Приложение сможет использовать низкоуровневые функции SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"Чтение данных в буфере кадров"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Приложение сможет считывать содержание буфера фреймов."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"настраивать экраны, подключенные через Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Приложение сможет подключаться к экранам с помощью Wi-Fi и настраивать их."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Управление мониторами, подключенными через Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Приложение сможет подключаться к базовому интерфейсу службы просмотра уведомлений. Это разрешение не используется обычными приложениями."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Правила выбора паролей"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролировать длину и символы при вводе паролей для снятия блокировки экрана."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Отслеживать попытки снятия блокировки экрана"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index f6a62cd..3806936 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Umožňuje aplikácii používať funkcie nízkej úrovne aplikácie SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"čítanie vyrovnávacej pamäte snímok"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Umožňuje aplikácii čítať obsah vyrovnávacej pamäte snímok."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurovať displeje cez sieť Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Umožňuje aplikácii konfigurovať displeje a pripojiť sa k nim cez siete Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"ovládať displeje cez sieť Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Umožňuje držiteľovi naviazať sa na najvyššiu úroveň služby na počúvanie upozornení. Bežné aplikácie by toto nastavenie nemali nikdy požadovať."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Nastaviť pravidlá pre heslo"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Ovládanie dĺžky hesiel na odomknutie obrazovky a v nich používané znaky."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Sledovať pokusy o odomknutie obrazovky"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index ffbae22..fcc72bf 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Programu omogoča uporabo funkcij nizke ravni SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"branje grafičnega/slikovnega medpomnilnika"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Programu omogoča branje vsebine grafičnega/slikovnega medpomnilnika."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfiguriranje zaslonov Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Omogoča aplikaciji konfiguriranje zaslonov Wi-Fi in povezovanje z njimi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"nadzor zaslonov Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Lastniku omogoča povezovanje z vmesnikom storitve poslušalca obvestil najvišje ravni. Tega nikoli ni treba uporabiti za navadne aplikacije."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Nastavitev pravil za geslo"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Nadzor nad dolžino in znaki, ki so dovoljeni v geslih za odklepanje zaslona."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"nadzor nad poskusi odklepanja zaslona"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 7465798..67de984 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Дозвољава апликацији да користи SurfaceFlinger функције ниског нивоа."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"читање бафера кадрова"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Дозвољава апликацији да чита садржај међумеморије кадрова."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"конфигурисање Wi-Fi екрана"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Дозвољава апликацији да конфигурише Wi-Fi екране и повезује се са њима."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"контрола Wi-Fi екрана"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Дозвољава власнику да се повеже са интерфејсом услуге монитора обавештења највишег нивоа. Уобичајене апликације никада не би требало да је користе."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Подешавање правила за лозинку"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролишите дужину и знакове дозвољене у лозинкама за откључавање екрана."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Надгледање покушаја откључавања екрана"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 5e39d66..5da8a63 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Tillåter att appen använder lågnivåfunktioner i SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"läsa rambuffert"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Tillåter att appen läser innehållet i rambufferten."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"konfigurerar Wi-Fi-skärmar"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Tillåter att appen konfigurerar och ansluter till Wi-Fi-skärmar."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"kontrollerar Wi-Fi-skärmar"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Innehavaren tillåts att binda till den översta nivåns gränssnitt för en meddelandelyssnare. Ska inte behövas för vanliga appar."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Ange lösenordsregler"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Bestäm hur många och vilka tecken som är tillåtna i skärmlåsets lösenord."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Övervaka försök att låsa upp skärmen"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 29ef9b3..a6f1ee3 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Inaruhusu programu kutumia vipengee vya kiwango cha chini vya SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"soma bafa ya fremu"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Inaruhusu programu kusoma maudhui ya fremu ya bafa."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"sanidi maonyesho ya Wifi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Inaruhusu programu kusanidi na kuunganika kwenye maonyesho ya Wifi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"dhibiti maonyesho ya Wifi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Inaruhusu kishikilizi kuunganishwa kwenye kusano cha kiwango cha juu cha huduma ya kisikilizi cha arifa. Haipaswi kuhitajika tena kwa programu za kawaida."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Weka kanuni za nenosiri"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Dhibiti urefu na vibambo vinavyoruhusiwa katika manenosiri ya kufungua skrini."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Chunguza majaribio ya kutofun gua skrini"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 9087783..f1d4c3c 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"อนุญาตให้แอปพลิเคชันใช้คุณลักษณะระดับต่ำของ SurfaceFlinger"</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"อ่านเฟรมบัฟเฟอร์"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"อนุญาตให้แอปพลิเคชันอ่านเนื้อหาในเฟรมบัฟเฟอร์"</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"กำหนดค่าการแสดงผลด้วย WiFi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"อนุญาตให้แอปกำหนดค่าและเชื่อมต่อกับจอแสดงผล WiFi ได้"</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"ควบคุมการแสดงผลด้วย WiFi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"อนุญาตให้เจ้าของเชื่อมโยงกับอินเตอร์เฟซระดับสูงสุดของบริการตัวฟังการแจ้งเตือน ซึ่งไม่มีความจำเป็นสำหรับแอปธรรมดา"</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"ตั้งค่ากฎรหัสผ่าน"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"ควบคุมความยาวและอักขระที่อนุญาตให้ใช้ในรหัสผ่านการปลดล็อกหน้าจอ"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"ตรวจสอบความพยายามในการปลดล็อกหน้าจอ"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index d30fb77..2d9ed63 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Pinapayagan ang app na gamitin ang mababang antas na mga tampok ng SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"basahin ang buffer ng frame"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Pinapayagan ang app na basahin ang nilalaman ng buffer ng frame."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"mag-configure ng mga Wifi display"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Pinapayagan ang app na mag-configure at kumonekta sa mga Wifi display."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"magkontrol ng mga Wifi display"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Nagbibigay-daan sa may-ari na mapailalim sa interface sa tuktok na antas ng isang serbisyo ng notification listener. Hindi dapat kailanganin para sa karaniwang apps kahit kailan."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Magtakda ng mga panuntunan sa password"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kontrolin ang haba at mga character na pinapayagan sa mga password sa pag-unlock ng screen."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Subaybayan ang mga pagsubok sa pag-unlock ng screen"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 30691d9..87c3c48 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Uygulamaya, SurfaceFlinger\'a ait düşük düzey özellikleri kullanma izni verir."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"çerçeve arabelleğini oku"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Uygulamaya, çerçeve arabelleğinin içeriğini okuma izni verir."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"Kablosuz ekranları yapılandır"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Uygulamaya kablosuz ekranları yapılandırma ve bunlara bağlanma izni verir."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"Kablosuz ekranları denetle"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"İzin sahibine bir bildirim dinleyici hizmetinin en üst düzey arayüzüne bağlanma izni verir. Normal uygulamalarda hiçbir zaman gerek duyulmaz."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Şifre kuralları ayarla"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Ekran kilidini açma şifrelerinde izin verilen uzunluğu ve karakterleri denetleme."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Ekran kilidini açma denemelerini izle"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index db27c85..099fb91 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Дозволяє програмі використовувати низькорівневі функції SurfaceFlinger."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"читати фрейм-буфер"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Дозволяє програмі читати вміст буфера кадрів."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"налаштувати екрани Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Дозволяє програмі налаштовувати екрани Wi-Fi і під’єднуватися до них."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"керувати екранами Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Дозволяє власнику прив’язуватися до інтерфейсу верхнього рівня служби читання сповіщень. Ніколи не застосовується для звичайних програм."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Устан. правила пароля"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Контролювати довжину паролів для розблокування екрана та дозволені в них символи."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Відстежув. спроби розблок. екрана"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 3002bcd..6f339e6 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Cho phép ứng dụng sử dụng các tính năng SurfaceFlinger cấp độ thấp."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"đọc bộ đệm khung"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Cho phép ứng dụng đọc nội dung của bộ đệm khung."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"định cấu hình màn hình Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Cho phép ứng dụng định cấu hình và kết nối với màn hình Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"kiểm soát màn hình Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Cho phép chủ sở hữu liên kết với giao diện cấp cao nhất của dịch vụ trình xử lý thông báo. Không cần thiết cho các ứng dụng thông thường."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Đặt quy tắc mật khẩu"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Kiểm soát độ dài và ký tự được phép trong mật khẩu mở khóa màn hình."</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Giám sát những lần thử mở khóa màn hình"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 52526de..d39cd3f 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"允许应用使用 SurfaceFlinger 低级功能。"</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"读取帧缓冲区"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"允许应用读取帧缓冲区的内容。"</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"配置 WLAN 显示设备"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"允许应用配置并连接到 WLAN 显示设备。"</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"控制 WLAN 显示设备"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"允许应用绑定到通知侦听器服务的顶级接口(普通应用绝不需要此权限)。"</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"设置密码规则"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"控制屏幕解锁密码所允许的长度和字符。"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"监视屏幕解锁尝试次数"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 922a4f5..a9f7b7b 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"允許應用程式使用 SurfaceFlinger 的低階功能。"</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"讀取框架緩衝"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"允許應用程式讀取畫面緩衝區的內容。"</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"設定 Wi-Fi 顯示裝置"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"允許應用程式設定及連接 Wi-Fi 顯示裝置。"</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"控制 Wi-Fi 顯示裝置"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"允許應用程式繫結至通知接聽器服務的頂層介面 (一般應用程式不需使用)。"</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"設定密碼規則"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"控制螢幕解鎖密碼所允許的長度和字元。"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"監視螢幕解鎖嘗試次數"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 3eaf19f..2957366 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -470,6 +470,10 @@
     <string name="permdesc_accessSurfaceFlinger" msgid="1041619516733293551">"Ivumela insiza ukuthi isebenzise okuqukethwe i-SurfaceFlinger okusezingeni eliphansi."</string>
     <string name="permlab_readFrameBuffer" msgid="6690504248178498136">"funda isikhumbuli sesikhashana sendikimba"</string>
     <string name="permdesc_readFrameBuffer" msgid="4937405521809454680">"Ivumela insiza ukuthi ifunde okuqukethwe ifreyimu yebhafa."</string>
+    <!-- no translation found for permlab_accessInputFlinger (5348635270689553857) -->
+    <skip />
+    <!-- no translation found for permdesc_accessInputFlinger (2104864941201226616) -->
+    <skip />
     <string name="permlab_configureWifiDisplay" msgid="5595661694746742168">"lungisa ukubukwa kwe-Wi-Fi"</string>
     <string name="permdesc_configureWifiDisplay" msgid="7916815158690218065">"Ivumela uhlelo lokusebenza ukulungisa nokuxhuma ekubukisweni kwe-Wi-Fi."</string>
     <string name="permlab_controlWifiDisplay" msgid="393641276723695496">"lawula ukubukwa kwe-Wi-Fi"</string>
@@ -639,10 +643,18 @@
     <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>
     <string name="permdesc_bindNotificationListenerService" msgid="985697918576902986">"Ivumela umbambi ukubophezela kwisixhumi esibonakalayo sezinga eliphezulu lesevisi yomlaleli wesaziso. Akusoze kwadingeka kwizinhlelo zokusebenza ezivamile."</string>
+    <!-- no translation found for permlab_invokeCarrierSetup (3699600833975117478) -->
+    <skip />
+    <!-- no translation found for permdesc_invokeCarrierSetup (4159549152529111920) -->
+    <skip />
     <string name="policylab_limitPassword" msgid="4497420728857585791">"Misa imithetho yephasiwedi"</string>
     <string name="policydesc_limitPassword" msgid="3252114203919510394">"Lawula ubude nezinhlamvu ezivunyelwe kumaphasiwedi okuvula isikrini"</string>
     <string name="policylab_watchLogin" msgid="914130646942199503">"Gaka imizamo yokuvula isikrini"</string>
@@ -1593,6 +1605,10 @@
     <skip />
     <!-- no translation found for mediaSize_na_tabloid (5775966416333578127) -->
     <skip />
+    <!-- no translation found for write_fail_reason_cancelled (7091258378121627624) -->
+    <skip />
+    <!-- no translation found for write_fail_reason_cannot_write (8132505417935337724) -->
+    <skip />
     <!-- no translation found for restr_pin_create_pin (8017600000263450337) -->
     <skip />
     <!-- no translation found for restr_pin_enter_pin (3395953421368476103) -->
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9918d85..181793f 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1550,6 +1550,7 @@
         <enum name="KEYCODE_ASSIST" value="219" />
         <enum name="KEYCODE_BRIGHTNESS_DOWN" value="220" />
         <enum name="KEYCODE_BRIGHTNESS_UP" value="221" />
+        <enum name="KEYCODE_MEDIA_AUDIO_TRACK" value="222" />
     </attr>
 
     <!-- ***************************************************************** -->
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0080ad9..b01b50e 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1371,6 +1371,11 @@
     <string name="permdesc_readFrameBuffer">Allows the app to read the content of the frame buffer.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_accessInputFlinger">access InputFlinger</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_accessInputFlinger">Allows the app to use InputFlinger low-level features.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_configureWifiDisplay">configure Wifi displays</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_configureWifiDisplay">Allows the app to configure and connect to Wifi displays.</string>
@@ -1853,6 +1858,11 @@
     <string name="permdesc_modifyNetworkAccounting">Allows the app to modify how network usage is accounted against apps. Not for use by normal apps.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_markNetworkSocket">modify socket marks</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_markNetworkSocket">Allows the app to modify socket marks for routing</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_accessNotifications">access notifications</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_accessNotifications">Allows the app to retrieve, examine, and clear notifications, including those posted by other apps.</string>
@@ -1862,6 +1872,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_bindNotificationListenerService">Allows the holder to bind to the top-level interface of a notification listener service. Should never be needed for normal apps.</string>
 
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_invokeCarrierSetup">invoke the carrier-provided configuration app</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_invokeCarrierSetup">Allows the holder to invoke the carrier-provided configuration app. Should never be needed for normal apps.</string>
+
     <!-- Policy administration -->
 
     <!-- Title of policy access to limiting the user's password choices -->
@@ -4154,94 +4169,103 @@
 
     <!-- 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 entry dialog label/hint for PIN [CHAR LIMIT=none] -->
+    <string name="restr_pin_enter_pin">Enter PIN</string>
+    <!-- PIN entry dialog label/hint for old PIN [CHAR LIMIT=none] -->
+    <string name="restr_pin_enter_old_pin">Current PIN</string>
+    <!-- PIN entry dialog label for new PIN [CHAR LIMIT=none] -->
+    <string name="restr_pin_enter_new_pin">New PIN</string>
+    <!-- PIN entry dialog label for new PIN confirmation [CHAR LIMIT=none] -->
+    <string name="restr_pin_confirm_pin">Confirm new PIN</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] -->
-    <string name="restr_pin_enter_pin">Enter PIN</string>
-    <!-- PIN entry dialog label for PIN confirmation [CHAR LIMIT=none] -->
-    <string name="restr_pin_confirm_pin">Confirm PIN</string>
     <!-- PIN entry dialog error when PINs are not the same [CHAR LIMIT=none] -->
     <string name="restr_pin_error_doesnt_match">PINs don\'t match. Try again.</string>
     <!-- PIN entry dialog error when PIN is too short [CHAR LIMIT=none] -->
@@ -4252,4 +4276,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..8a12ac8 100755
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -214,8 +214,9 @@
   <java-symbol type="id" name="breadcrumb_section" />
   <java-symbol type="id" name="action_bar_spinner" />
   <java-symbol type="id" name="pin_message" />
-  <java-symbol type="id" name="pin1_text" />
-  <java-symbol type="id" name="pin2_text" />
+  <java-symbol type="id" name="pin_text" />
+  <java-symbol type="id" name="pin_new_text" />
+  <java-symbol type="id" name="pin_confirm_text" />
   <java-symbol type="id" name="pin_error_message" />
 
   <java-symbol type="attr" name="actionModeShareDrawable" />
@@ -907,6 +908,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" />
@@ -1155,7 +1158,8 @@
   <java-symbol type="layout" name="sms_short_code_confirmation_dialog" />
   <java-symbol type="layout" name="action_bar_up_container" />
   <java-symbol type="layout" name="app_not_authorized" />
-  <java-symbol type="layout" name="pin_challenge" />
+  <java-symbol type="layout" name="restrictions_pin_challenge" />
+  <java-symbol type="layout" name="restrictions_pin_setup" />
 
   <java-symbol type="anim" name="slide_in_child_bottom" />
   <java-symbol type="anim" name="slide_in_right" />
diff --git a/core/res/res/xml/audio_assets.xml b/core/res/res/xml/audio_assets.xml
index 746dbab..af5798a 100644
--- a/core/res/res/xml/audio_assets.xml
+++ b/core/res/res/xml/audio_assets.xml
@@ -34,5 +34,6 @@
         <asset id="FX_KEYPRESS_SPACEBAR" file="KeypressSpacebar.ogg"/>
         <asset id="FX_KEYPRESS_DELETE" file="KeypressDelete.ogg"/>
         <asset id="FX_KEYPRESS_RETURN" file="KeypressReturn.ogg"/>
+        <asset id="FX_KEYPRESS_INVALID" file="KeypressInvalid.ogg"/>
     </group>
 </audio_assets>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 1289971..ec8e7ea 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -54,10 +54,6 @@
         <group gid="inet" />
     </permission>
 
-    <permission name="android.permission.CAMERA" >
-        <group gid="camera" />
-    </permission>
-
     <permission name="android.permission.READ_LOGS" >
         <group gid="log" />
     </permission>
diff --git a/data/sounds/AllAudio.mk b/data/sounds/AllAudio.mk
index 8b03bf7..ed9bea5 100644
--- a/data/sounds/AllAudio.mk
+++ b/data/sounds/AllAudio.mk
@@ -221,6 +221,7 @@
     $(LOCAL_PATH)/effects/ogg/KeypressReturn_120_48k.ogg:system/media/audio/ui/KeypressReturn.ogg \
     $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_120_48k.ogg:system/media/audio/ui/KeypressSpacebar.ogg \
     $(LOCAL_PATH)/effects/ogg/KeypressStandard_120_48k.ogg:system/media/audio/ui/KeypressStandard.ogg \
+    $(LOCAL_PATH)/effects/ogg/KeypressInvalid_120_48k.ogg:system/media/audio/ui/KeypressInvalid.ogg \
     $(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \
     $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
     $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
diff --git a/data/sounds/AudioPackage11.mk b/data/sounds/AudioPackage11.mk
index e16a92d..2897b04 100644
--- a/data/sounds/AudioPackage11.mk
+++ b/data/sounds/AudioPackage11.mk
@@ -43,7 +43,7 @@
 	$(LOCAL_PATH)/notifications/ogg/Spica.ogg:system/media/audio/notifications/Spica.ogg \
 	$(LOCAL_PATH)/notifications/ogg/Syrma.ogg:system/media/audio/notifications/Syrma.ogg \
 	$(LOCAL_PATH)/notifications/ogg/Talitha.ogg:system/media/audio/notifications/Talitha.ogg \
-	$(LOCAL_PATH)/notifications/ogg/Tejat.ogg:system/media/audio/notifications/Tejat.ogg \
+	$(LOCAL_PATH)/notifications/ogg/Tejat_proc48.ogg:system/media/audio/notifications/Tejat.ogg \
 	$(LOCAL_PATH)/notifications/ogg/Vega.ogg:system/media/audio/notifications/Vega.ogg \
 	$(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \
 	$(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \
diff --git a/data/sounds/effects/KeypressInvalid.ogg b/data/sounds/effects/KeypressInvalid.ogg
new file mode 100644
index 0000000..24935ad
--- /dev/null
+++ b/data/sounds/effects/KeypressInvalid.ogg
Binary files differ
diff --git a/data/sounds/effects/KeypressInvalid.wav b/data/sounds/effects/KeypressInvalid.wav
new file mode 100644
index 0000000..c4180e35
--- /dev/null
+++ b/data/sounds/effects/KeypressInvalid.wav
Binary files differ
diff --git a/data/sounds/effects/ogg/KeypressInvalid_120_48k.ogg b/data/sounds/effects/ogg/KeypressInvalid_120_48k.ogg
new file mode 100644
index 0000000..24935ad
--- /dev/null
+++ b/data/sounds/effects/ogg/KeypressInvalid_120_48k.ogg
Binary files differ
diff --git a/data/sounds/notifications/ogg/Tejat_proc48.ogg b/data/sounds/notifications/ogg/Tejat_proc48.ogg
new file mode 100644
index 0000000..b1637d7
--- /dev/null
+++ b/data/sounds/notifications/ogg/Tejat_proc48.ogg
Binary files differ
diff --git a/data/sounds/notifications/wav/Deneb_processed_48kHz.wav b/data/sounds/notifications/wav/Deneb_processed_48kHz.wav
new file mode 100644
index 0000000..e2df8e9
--- /dev/null
+++ b/data/sounds/notifications/wav/Deneb_processed_48kHz.wav
Binary files differ
diff --git a/docs/html/about/versions/android-4.3.jd b/docs/html/about/versions/android-4.3.jd
index 0ca3bc6..d0ccfbe 100644
--- a/docs/html/about/versions/android-4.3.jd
+++ b/docs/html/about/versions/android-4.3.jd
@@ -7,7 +7,7 @@
 
 <div id="qv-wrapper">
 <div id="qv">
-  
+
 <h2>In this document
     <a href="#" onclick="hideNestedItems('#toc43',this);return false;" class="header-toggle">
         <span class="more">show more</span>
@@ -62,7 +62,6 @@
   </li>
   <li><a href="#UserInput">User Input</a>
     <ol>
-      <li><a href="#SignificantMotion">Detect significant motion</a></li>
       <li><a href="#Sensors">New sensor types</a></li>
     </ol>
   </li>
@@ -133,7 +132,7 @@
 Then build your apps against the Android {@sdkPlatformVersion} platform to begin using the
 latest APIs.</p>
 
-  
+
 <h3 id="ApiLevel">Update your target API level</h3>
 
 <p>To better optimize your app for devices running Android {@sdkPlatformVersion},
@@ -145,7 +144,7 @@
 <p>You can use APIs in Android {@sdkPlatformVersion} while also supporting older versions by adding
 conditions to your code that check for the system API level before executing
 APIs not supported by your <a
-href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a>. 
+href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a>.
 To learn more about maintaining backward compatibility, read <a
 href="{@docRoot}training/basics/supporting-devices/platforms.html">Supporting Different
 Platform Versions</a>.</p>
@@ -172,11 +171,11 @@
 
 <p>Your app might misbehave in a restricted profile environment.</p>
 
-<p>Users in a <a href="#RestrictedProfiles">restricted profile</a> environment might not 
-have all the standard Android apps available. For example, a restricted profile might have the 
-web browser and camera app disabled. So your app should not make assumptions about which apps are 
-available, because if you call {@link android.app.Activity#startActivity startActivity()} without 
-verifying whether an app is available to handle the {@link android.content.Intent}, 
+<p>Users in a <a href="#RestrictedProfiles">restricted profile</a> environment might not
+have all the standard Android apps available. For example, a restricted profile might have the
+web browser and camera app disabled. So your app should not make assumptions about which apps are
+available, because if you call {@link android.app.Activity#startActivity startActivity()} without
+verifying whether an app is available to handle the {@link android.content.Intent},
 your app might crash in a restricted profile.</p>
 
 <p>When using an implicit intent, you should always verify that an app is available to handle the intent by calling {@link android.content.Intent#resolveActivity resolveActivity()} or {@link android.content.pm.PackageManager#queryIntentActivities queryIntentActivities()}. For example:</p>
@@ -197,20 +196,20 @@
 <p>Your app might misbehave in a restricted profile environment.</p>
 
 <p>Users within a restricted profile environment do not have access to user accounts by default.
-If your app depends on an {@link android.accounts.Account}, then your app might crash or behave 
+If your app depends on an {@link android.accounts.Account}, then your app might crash or behave
 unexpectedly when used in a restricted profile.</p>
 
 <p>If you'd like to prevent restricted profiles from using your app entirely because your
-app depends on account information that's sensitive, specify the <a 
-href="{@docRoot}guide/topics/manifest/application-element.html#requiredAccountType">{@code 
-android:requiredAccountType}</a> attribute in your manifest's <a 
-href="{@docRoot}guide/topics/manifest/application-element.html">{@code &lt;application>}</a> 
+app depends on account information that's sensitive, specify the <a
+href="{@docRoot}guide/topics/manifest/application-element.html#requiredAccountType">{@code
+android:requiredAccountType}</a> attribute in your manifest's <a
+href="{@docRoot}guide/topics/manifest/application-element.html">{@code &lt;application>}</a>
 element.</p>
 
-<p>If you’d like to allow restricted profiles to continue using your app even though they can’t 
-create their own accounts, then you can either disable your app features that require an account 
+<p>If you’d like to allow restricted profiles to continue using your app even though they can’t
+create their own accounts, then you can either disable your app features that require an account
 or allow restricted profiles to access the accounts created by the primary user. For more
-information, see the section 
+information, see the section
 below about <a href="#AccountsInProfile">Supporting accounts in a restricted profile</a>.</p>
 
 
@@ -218,67 +217,67 @@
 
 <h2 id="RestrictedProfiles">Restricted Profiles</h2>
 
-<p>On Android tablets, users can now create restricted profiles based on the primary user. 
+<p>On Android tablets, users can now create restricted profiles based on the primary user.
 When users create a restricted profile, they can enable restrictions such as which apps are
 available to the profile. A new set of APIs in Android 4.3 also allow you to build fine-grain
-restriction settings for the apps you develop. For example, by using the new APIs, you can 
-allow users to control what type of content is available within your app when running in a 
+restriction settings for the apps you develop. For example, by using the new APIs, you can
+allow users to control what type of content is available within your app when running in a
 restricted profile environment.</p>
 
-<p>The UI for users to control the restrictions you've built is managed by the system's 
+<p>The UI for users to control the restrictions you've built is managed by the system's
 Settings application. To make your app's restriction settings appear to the user,
-you must declare the restrictions your app provides by creating a {@link 
-android.content.BroadcastReceiver} that receives the {@link android.content.Intent#ACTION_GET_RESTRICTION_ENTRIES} intent. The system invokes this intent to query 
-all apps for available restrictions, then builds the UI to allow the primary user to 
+you must declare the restrictions your app provides by creating a {@link
+android.content.BroadcastReceiver} that receives the {@link android.content.Intent#ACTION_GET_RESTRICTION_ENTRIES} intent. The system invokes this intent to query
+all apps for available restrictions, then builds the UI to allow the primary user to
 manage restrictions for each restricted profile. </p>
 
-<p>In the {@link android.content.BroadcastReceiver#onReceive onReceive()} method of 
-your {@link android.content.BroadcastReceiver}, you must create a {@link 
-android.content.RestrictionEntry} for each restriction your app provides. Each {@link 
-android.content.RestrictionEntry} defines a restriction title, description, and one of the 
+<p>In the {@link android.content.BroadcastReceiver#onReceive onReceive()} method of
+your {@link android.content.BroadcastReceiver}, you must create a {@link
+android.content.RestrictionEntry} for each restriction your app provides. Each {@link
+android.content.RestrictionEntry} defines a restriction title, description, and one of the
 following data types:</p>
 
 <ul>
-  <li>{@link android.content.RestrictionEntry#TYPE_BOOLEAN} for a restriction that is 
+  <li>{@link android.content.RestrictionEntry#TYPE_BOOLEAN} for a restriction that is
   either true or false.
-  <li>{@link android.content.RestrictionEntry#TYPE_CHOICE} for a restriction that has 
+  <li>{@link android.content.RestrictionEntry#TYPE_CHOICE} for a restriction that has
   multiple choices that are mutually exclusive (radio button choices).
-  <li>{@link android.content.RestrictionEntry#TYPE_MULTI_SELECT} for a restriction that 
+  <li>{@link android.content.RestrictionEntry#TYPE_MULTI_SELECT} for a restriction that
   has multiple choices that are <em>not</em> mutually exclusive (checkbox choices).
 </ul>
 
-<p>You then put all the {@link android.content.RestrictionEntry} objects into an {@link 
-java.util.ArrayList} and put it into the broadcast receiver's result as the value for the 
+<p>You then put all the {@link android.content.RestrictionEntry} objects into an {@link
+java.util.ArrayList} and put it into the broadcast receiver's result as the value for the
 {@link android.content.Intent#EXTRA_RESTRICTIONS_LIST} extra.</p>
 
-<p>The system creates the UI for your app's restrictions in the Settings app and saves each 
-restriction with the unique key you provided for each {@link android.content.RestrictionEntry} 
-object. When the user opens your app, you can query for any current restrictions by 
-calling {@link android.os.UserManager#getApplicationRestrictions getApplicationRestrictions()}. 
+<p>The system creates the UI for your app's restrictions in the Settings app and saves each
+restriction with the unique key you provided for each {@link android.content.RestrictionEntry}
+object. When the user opens your app, you can query for any current restrictions by
+calling {@link android.os.UserManager#getApplicationRestrictions getApplicationRestrictions()}.
 This returns a {@link android.os.Bundle} containing the key-value pairs for each restriction
 you defined with the {@link android.content.RestrictionEntry} objects.</p>
 
-<p>If you want to provide more specific restrictions that can't be handled by boolean, single 
-choice, and multi-choice values, then you can create an activity where the user can specify the 
-restrictions and allow users to open that activity from the restriction settings. In your 
-broadcast receiver, include the {@link android.content.Intent#EXTRA_RESTRICTIONS_INTENT} extra 
+<p>If you want to provide more specific restrictions that can't be handled by boolean, single
+choice, and multi-choice values, then you can create an activity where the user can specify the
+restrictions and allow users to open that activity from the restriction settings. In your
+broadcast receiver, include the {@link android.content.Intent#EXTRA_RESTRICTIONS_INTENT} extra
 in the result {@link android.os.Bundle}. This extra must specify an {@link android.content.Intent}
-indicating the {@link android.app.Activity} class to launch (use the 
-{@link android.os.Bundle#putParcelable putParcelable()} method to pass {@link 
+indicating the {@link android.app.Activity} class to launch (use the
+{@link android.os.Bundle#putParcelable putParcelable()} method to pass {@link
 android.content.Intent#EXTRA_RESTRICTIONS_INTENT} with the intent).
-When the primary user enters your activity to set custom restrictions, your 
-activity must then return a result containing the restriction values in an extra using either 
-the {@link android.content.Intent#EXTRA_RESTRICTIONS_LIST} or {@link 
+When the primary user enters your activity to set custom restrictions, your
+activity must then return a result containing the restriction values in an extra using either
+the {@link android.content.Intent#EXTRA_RESTRICTIONS_LIST} or {@link
 android.content.Intent#EXTRA_RESTRICTIONS_BUNDLE} key, depending on whether you specify
 {@link android.content.RestrictionEntry} objects or key-value pairs, respectively.</p>
 
 
 <h3 id="AccountsInProfile">Supporting accounts in a restricted profile</h3>
 
-<p>Any accounts added to the primary user are available to a restricted profile, but the 
-accounts are not accessible from the {@link android.accounts.AccountManager} APIs by default. 
+<p>Any accounts added to the primary user are available to a restricted profile, but the
+accounts are not accessible from the {@link android.accounts.AccountManager} APIs by default.
 If you attempt to add an account with {@link android.accounts.AccountManager} while in a restricted
-profile, you will get a failure result. Due to these restrictions, you have the following 
+profile, you will get a failure result. Due to these restrictions, you have the following
 three options:</p>
 
 <li><strong>Allow access to the owner’s accounts from a restricted profile.</strong>
@@ -289,21 +288,25 @@
     android:restrictedAccountType="com.example.account.type" >
 </pre>
 
-<p class="caution"><strong>Caution:</strong> Enabling this attribute provides your 
-app access to the primary user's accounts from restricted profiles. So you should allow this 
+<p class="caution"><strong>Caution:</strong> Enabling this attribute provides your
+app access to the primary user's accounts from restricted profiles. So you should allow this
 only if the information displayed by your app does not reveal personally identifiable
-information (PII) that’s considered sensitive.</p>
+information (PII) that’s considered sensitive. The system settings will inform the primary
+user that your app grants restricted profiles to their accounts, so it should be clear to the user
+that account access is important for your app's functionality. If possible, you should also
+provide adequate restriction controls for the primary user that define how much account access
+is allowed in your app.</p>
 </li>
 
 
 <li><strong>Disable certain functionality when unable to modify accounts.</strong>
-<p>If you want to use accounts, but don’t actually require them for your app’s primary 
-functionality, you can check for account availability and disable features when not available. 
-You should first check if there is an existing account available. If not, then query whether 
-it’s possible to create a new account by calling {@link 
-android.os.UserManager#getUserRestrictions()} and check the {@link 
-android.os.UserManager#DISALLOW_MODIFY_ACCOUNTS} extra in the result. If it is {@code true}, 
-then you should disable whatever functionality of your app requires access to accounts. 
+<p>If you want to use accounts, but don’t actually require them for your app’s primary
+functionality, you can check for account availability and disable features when not available.
+You should first check if there is an existing account available. If not, then query whether
+it’s possible to create a new account by calling {@link
+android.os.UserManager#getUserRestrictions()} and check the {@link
+android.os.UserManager#DISALLOW_MODIFY_ACCOUNTS} extra in the result. If it is {@code true},
+then you should disable whatever functionality of your app requires access to accounts.
 For example:</p>
 <pre>
 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
@@ -312,15 +315,15 @@
    // cannot add accounts, disable some functionality
 }
 </pre>
-<p class="note"><strong>Note:</strong> In this scenario, you should <em>not</em> declare 
+<p class="note"><strong>Note:</strong> In this scenario, you should <em>not</em> declare
 any new attributes in your manifest file.</p>
 </li>
 
 <li><strong>Disable your app when unable to access private accounts.</strong>
-<p>If it’s instead important that your app not be available to restricted profiles because 
-your app depends on sensitive personal information in an account (and because restricted profiles 
+<p>If it’s instead important that your app not be available to restricted profiles because
+your app depends on sensitive personal information in an account (and because restricted profiles
 currently cannot add new accounts), add
-the <a href="{@docRoot}guide/topics/manifest/application-element.html#requiredAccountType">{@code 
+the <a href="{@docRoot}guide/topics/manifest/application-element.html#requiredAccountType">{@code
 android:requiredAccountType}</a> attribute to the <a
 href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application></a> tag:</p>
 <pre>
@@ -338,11 +341,11 @@
 
 <h3 id="BTLE">Bluetooth Low Energy (Smart Ready)</h3>
 
-<p>Android now supports Bluetooth Low Energy (LE) with new APIs in {@link android.bluetooth}. 
-With the new APIs, you can build Android apps that communicate with Bluetooth Low Energy 
+<p>Android now supports Bluetooth Low Energy (LE) with new APIs in {@link android.bluetooth}.
+With the new APIs, you can build Android apps that communicate with Bluetooth Low Energy
 peripherals such as heart rate monitors and pedometers.</p>
 
-<p>Because Bluetooth LE is a hardware feature that is not available on all 
+<p>Because Bluetooth LE is a hardware feature that is not available on all
 Android-powered devices, you must declare in your manifest file a <a
 href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code &lt;uses-feature>}</a>
 element for {@code "android.hardware.bluetooth_le"}:</p>
@@ -350,11 +353,11 @@
 &lt;uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
 </pre>
 
-<p>If you're already familiar with Android's Classic Bluetooth APIs, notice that using the 
-Bluetooth LE APIs has some differences. Most importantly is that there's now a {@link 
-android.bluetooth.BluetoothManager} class that you should use for some high level operations 
-such as acquiring a {@link android.bluetooth.BluetoothAdapter}, getting a list of connected 
-devices, and checking the state of a device. For example, here's how you should now get the 
+<p>If you're already familiar with Android's Classic Bluetooth APIs, notice that using the
+Bluetooth LE APIs has some differences. Most importantly is that there's now a {@link
+android.bluetooth.BluetoothManager} class that you should use for some high level operations
+such as acquiring a {@link android.bluetooth.BluetoothAdapter}, getting a list of connected
+devices, and checking the state of a device. For example, here's how you should now get the
 {@link android.bluetooth.BluetoothAdapter}:</p>
 <pre>
 final BluetoothManager bluetoothManager =
@@ -362,32 +365,32 @@
 mBluetoothAdapter = bluetoothManager.getAdapter();
 </pre>
 
-<p>To discover Bluetooth LE peripherals, call {@link android.bluetooth.BluetoothAdapter#startLeScan 
-startLeScan()} on the {@link android.bluetooth.BluetoothAdapter}, passing it an implementation 
-of the {@link android.bluetooth.BluetoothAdapter.LeScanCallback} interface. When the Bluetooth 
-adapter detects a Bluetooth LE peripheral, your {@link 
-android.bluetooth.BluetoothAdapter.LeScanCallback} implementation receives a call to the 
-{@link android.bluetooth.BluetoothAdapter.LeScanCallback#onLeScan onLeScan()} method. This 
-method provides you with a {@link android.bluetooth.BluetoothDevice} object representing the 
-detected device, the RSSI value for the device, and a byte array containing the device's 
+<p>To discover Bluetooth LE peripherals, call {@link android.bluetooth.BluetoothAdapter#startLeScan
+startLeScan()} on the {@link android.bluetooth.BluetoothAdapter}, passing it an implementation
+of the {@link android.bluetooth.BluetoothAdapter.LeScanCallback} interface. When the Bluetooth
+adapter detects a Bluetooth LE peripheral, your {@link
+android.bluetooth.BluetoothAdapter.LeScanCallback} implementation receives a call to the
+{@link android.bluetooth.BluetoothAdapter.LeScanCallback#onLeScan onLeScan()} method. This
+method provides you with a {@link android.bluetooth.BluetoothDevice} object representing the
+detected device, the RSSI value for the device, and a byte array containing the device's
 advertisement record.</p>
 
-<p>If you want to scan for only specific types of peripherals, you can instead call {@link 
-android.bluetooth.BluetoothAdapter#startLeScan startLeScan()} and include an array of {@link 
+<p>If you want to scan for only specific types of peripherals, you can instead call {@link
+android.bluetooth.BluetoothAdapter#startLeScan startLeScan()} and include an array of {@link
 java.util.UUID} objects that specify the GATT services your app supports.</p>
 
-<p class="note"><strong>Note:</strong> You can only scan for Bluetooth LE devices <em>or</em> 
-scan for Classic Bluetooth devices using previous APIs. You cannot scan for both LE and Classic 
+<p class="note"><strong>Note:</strong> You can only scan for Bluetooth LE devices <em>or</em>
+scan for Classic Bluetooth devices using previous APIs. You cannot scan for both LE and Classic
 Bluetooth devices at once.</p>
 
-<p>To then connect to a Bluetooth LE peripheral, call {@link 
-android.bluetooth.BluetoothDevice#connectGatt connectGatt()} on the corresponding 
-{@link android.bluetooth.BluetoothDevice} object, passing it an implementation of 
-{@link android.bluetooth.BluetoothGattCallback}. Your implementation of {@link 
-android.bluetooth.BluetoothGattCallback} receives callbacks regarding the connectivity 
-state with the device and other events. It's during the {@link 
-android.bluetooth.BluetoothGattCallback#onConnectionStateChange onConnectionStateChange()} 
-callback that you can begin communicating with the device if the method passes {@link 
+<p>To then connect to a Bluetooth LE peripheral, call {@link
+android.bluetooth.BluetoothDevice#connectGatt connectGatt()} on the corresponding
+{@link android.bluetooth.BluetoothDevice} object, passing it an implementation of
+{@link android.bluetooth.BluetoothGattCallback}. Your implementation of {@link
+android.bluetooth.BluetoothGattCallback} receives callbacks regarding the connectivity
+state with the device and other events. It's during the {@link
+android.bluetooth.BluetoothGattCallback#onConnectionStateChange onConnectionStateChange()}
+callback that you can begin communicating with the device if the method passes {@link
 android.bluetooth.BluetoothProfile#STATE_CONNECTED} as the new state.</p>
 
 <p>Accessing Bluetooth features on a device also requires that your app request certain
@@ -397,39 +400,39 @@
 
 <h3 id="WiFiScan">Wi-Fi scan-only mode</h3>
 
-<p>When attempting to identify the user's location, Android may use Wi-Fi to help determine 
-the location by scanning nearby access points. However, users often keep Wi-Fi turned off to 
-conserve battery, resulting in location data that's less accurate. Android now includes a 
-scan-only mode that allows the device Wi-Fi to scan access points to help obtain the location 
+<p>When attempting to identify the user's location, Android may use Wi-Fi to help determine
+the location by scanning nearby access points. However, users often keep Wi-Fi turned off to
+conserve battery, resulting in location data that's less accurate. Android now includes a
+scan-only mode that allows the device Wi-Fi to scan access points to help obtain the location
 without connecting to an access point, thus greatly reducing battery usage.</p>
 
-<p>If you want to acquire the user's location but Wi-Fi is currently off, you can request the 
-user to enable Wi-Fi scan-only mode by calling {@link android.content.Context#startActivity 
-startActivity()} with the action {@link 
+<p>If you want to acquire the user's location but Wi-Fi is currently off, you can request the
+user to enable Wi-Fi scan-only mode by calling {@link android.content.Context#startActivity
+startActivity()} with the action {@link
 android.net.wifi.WifiManager#ACTION_REQUEST_SCAN_ALWAYS_AVAILABLE}.</p>
 
 
 <h3 id="WiFiConfig">Wi-Fi configuration</h3>
 
-<p>New {@link android.net.wifi.WifiEnterpriseConfig} APIs allow enterprise-oriented services to 
+<p>New {@link android.net.wifi.WifiEnterpriseConfig} APIs allow enterprise-oriented services to
 automate Wi-Fi configuration for managed devices.</p>
 
 
 <h3 id="QuickResponse">Quick response for incoming calls</h3>
 
-<p>Since Android 4.0, a feature called "Quick response" allows users to respond to incoming 
-calls with an immediate text message without needing to pick up the call or unlock the device. 
-Until now, these quick messages were always handled by the default Messaging app. Now any app 
-can declare its capability to handle these messages by creating a {@link android.app.Service} 
+<p>Since Android 4.0, a feature called "Quick response" allows users to respond to incoming
+calls with an immediate text message without needing to pick up the call or unlock the device.
+Until now, these quick messages were always handled by the default Messaging app. Now any app
+can declare its capability to handle these messages by creating a {@link android.app.Service}
 with an intent filter for {@link android.telephony.TelephonyManager#ACTION_RESPOND_VIA_MESSAGE}.</p>
 
-<p>When the user responds to an incoming call with a quick response, the Phone app sends 
-the {@link android.telephony.TelephonyManager#ACTION_RESPOND_VIA_MESSAGE} intent with a URI 
-describing the recipient (the caller) and the {@link android.content.Intent#EXTRA_TEXT} extra 
-with the message the user wants to send. When your service receives the intent, it should deliver 
+<p>When the user responds to an incoming call with a quick response, the Phone app sends
+the {@link android.telephony.TelephonyManager#ACTION_RESPOND_VIA_MESSAGE} intent with a URI
+describing the recipient (the caller) and the {@link android.content.Intent#EXTRA_TEXT} extra
+with the message the user wants to send. When your service receives the intent, it should deliver
 the message and immediately stop itself (your app should not show an activity).</p>
 
-<p>In order to receive this intent, you must declare the {@link 
+<p>In order to receive this intent, you must declare the {@link
 android.Manifest.permission#SEND_RESPOND_VIA_MESSAGE} permission.</p>
 
 
@@ -438,120 +441,120 @@
 
 <h3 id="DASH">MPEG DASH support</h3>
 
-<p>Android now supports Dynamic Adaptive Streaming over HTTP (DASH) in accordance with the 
-ISO/IEC 23009-1 standard, using existing APIs in {@link android.media.MediaCodec} and {@link 
-android.media.MediaExtractor}. The framework underlying these APIs has been updated to support 
-parsing of fragmented MP4 files, but your app is still responsible for parsing the MPD metadata 
+<p>Android now supports Dynamic Adaptive Streaming over HTTP (DASH) in accordance with the
+ISO/IEC 23009-1 standard, using existing APIs in {@link android.media.MediaCodec} and {@link
+android.media.MediaExtractor}. The framework underlying these APIs has been updated to support
+parsing of fragmented MP4 files, but your app is still responsible for parsing the MPD metadata
 and passing the individual streams to {@link android.media.MediaExtractor}.</p>
 
-<p>If you want to use DASH with encrypted content, notice that the {@link android.media.MediaExtractor#getSampleCryptoInfo getSampleCryptoInfo()} method returns the {@link 
-android.media.MediaCodec.CryptoInfo} metadata describing the structure of each encrypted media 
-sample. Also, the {@link android.media.MediaExtractor#getPsshInfo()} method has been added to 
-{@link android.media.MediaExtractor} so you can access the PSSH metadata for your DASH media. 
-This method returns a map of {@link java.util.UUID} objects to bytes, with the 
-{@link java.util.UUID} specifying the crypto scheme, and the bytes being the data specific 
+<p>If you want to use DASH with encrypted content, notice that the {@link android.media.MediaExtractor#getSampleCryptoInfo getSampleCryptoInfo()} method returns the {@link
+android.media.MediaCodec.CryptoInfo} metadata describing the structure of each encrypted media
+sample. Also, the {@link android.media.MediaExtractor#getPsshInfo()} method has been added to
+{@link android.media.MediaExtractor} so you can access the PSSH metadata for your DASH media.
+This method returns a map of {@link java.util.UUID} objects to bytes, with the
+{@link java.util.UUID} specifying the crypto scheme, and the bytes being the data specific
 to that scheme.</p>
 
 
 <h3 id="DRM">Media DRM</h3>
 
 <p>The new {@link android.media.MediaDrm} class provides a modular solution for digital rights
-management (DRM) with your media content by separating DRM concerns from media playback. For 
-instance, this API separation allows you to play back Widevine-encrypted content without having 
-to use the Widevine media format. This DRM solution also supports DASH Common Encryption so you 
+management (DRM) with your media content by separating DRM concerns from media playback. For
+instance, this API separation allows you to play back Widevine-encrypted content without having
+to use the Widevine media format. This DRM solution also supports DASH Common Encryption so you
 can use a variety of DRM schemes with your streaming content.</p>
 
-<p>You can use {@link android.media.MediaDrm} to obtain opaque key-request messages and process 
-key-response messages from the server for license acquisition and provisioning. Your app is 
-responsible for handling the network communication with the servers; the {@link 
+<p>You can use {@link android.media.MediaDrm} to obtain opaque key-request messages and process
+key-response messages from the server for license acquisition and provisioning. Your app is
+responsible for handling the network communication with the servers; the {@link
 android.media.MediaDrm} class provides only the ability to generate and process the messages.</p>
 
-<p>The {@link android.media.MediaDrm} APIs are  intended to be used in conjunction with the 
-{@link android.media.MediaCodec} APIs that were introduced in Android 4.1 (API level 16), 
-including {@link android.media.MediaCodec} for encoding and decoding your content, {@link 
-android.media.MediaCrypto} for handling encrypted content, and {@link android.media.MediaExtractor} 
+<p>The {@link android.media.MediaDrm} APIs are  intended to be used in conjunction with the
+{@link android.media.MediaCodec} APIs that were introduced in Android 4.1 (API level 16),
+including {@link android.media.MediaCodec} for encoding and decoding your content, {@link
+android.media.MediaCrypto} for handling encrypted content, and {@link android.media.MediaExtractor}
 for extracting and demuxing your content.</p>
 
-<p>You must first construct {@link android.media.MediaExtractor} and 
-{@link android.media.MediaCodec} objects. You can then access the DRM-scheme-identifying 
-{@link java.util.UUID}, typically from metadata in the content, and use it to construct an 
+<p>You must first construct {@link android.media.MediaExtractor} and
+{@link android.media.MediaCodec} objects. You can then access the DRM-scheme-identifying
+{@link java.util.UUID}, typically from metadata in the content, and use it to construct an
 instance of a {@link android.media.MediaDrm} object with its constructor.</p>
 
 
 <h3 id="EncodingSurface">Video encoding from a Surface</h3>
 
-<p>Android 4.1 (API level 16) added the {@link android.media.MediaCodec} class for low-level 
-encoding and decoding of media content. When encoding video, Android 4.1 required that you provide 
-the media with a {@link java.nio.ByteBuffer} array, but Android 4.3 now allows you to use a {@link 
-android.view.Surface} as the input to an encoder. For instance, this allows you to encode input 
+<p>Android 4.1 (API level 16) added the {@link android.media.MediaCodec} class for low-level
+encoding and decoding of media content. When encoding video, Android 4.1 required that you provide
+the media with a {@link java.nio.ByteBuffer} array, but Android 4.3 now allows you to use a {@link
+android.view.Surface} as the input to an encoder. For instance, this allows you to encode input
 from an existing video file or using frames generated from OpenGL ES.</p>
 
-<p>To use a {@link android.view.Surface} as the input to your encoder, first call {@link 
-android.media.MediaCodec#configure configure()} for your {@link android.media.MediaCodec}. 
-Then call {@link android.media.MediaCodec#createInputSurface()} to receive the {@link 
+<p>To use a {@link android.view.Surface} as the input to your encoder, first call {@link
+android.media.MediaCodec#configure configure()} for your {@link android.media.MediaCodec}.
+Then call {@link android.media.MediaCodec#createInputSurface()} to receive the {@link
 android.view.Surface} upon which you can stream your media.</p>
 
-<p>For example, you can use the given {@link android.view.Surface} as the window for an OpenGL 
-context by passing it to {@link android.opengl.EGL14#eglCreateWindowSurface 
-eglCreateWindowSurface()}. Then while rendering the surface, call {@link 
-android.opengl.EGL14#eglSwapBuffers eglSwapBuffers()} to pass the frame to the {@link 
+<p>For example, you can use the given {@link android.view.Surface} as the window for an OpenGL
+context by passing it to {@link android.opengl.EGL14#eglCreateWindowSurface
+eglCreateWindowSurface()}. Then while rendering the surface, call {@link
+android.opengl.EGL14#eglSwapBuffers eglSwapBuffers()} to pass the frame to the {@link
 android.media.MediaCodec}.</p>
 
-<p>To begin encoding, call {@link android.media.MediaCodec#start()} on the {@link 
-android.media.MediaCodec}. When done, call {@link android.media.MediaCodec#signalEndOfInputStream} 
-to terminate encoding, and call {@link android.view.Surface#release()} on the 
+<p>To begin encoding, call {@link android.media.MediaCodec#start()} on the {@link
+android.media.MediaCodec}. When done, call {@link android.media.MediaCodec#signalEndOfInputStream}
+to terminate encoding, and call {@link android.view.Surface#release()} on the
 {@link android.view.Surface}.</p>
 
 
 <h3 id="MediaMuxing">Media muxing</h3>
 
-<p>The new {@link android.media.MediaMuxer} class enables multiplexing between one audio stream 
-and one video stream. These APIs serve as a counterpart to the {@link android.media.MediaExtractor} 
+<p>The new {@link android.media.MediaMuxer} class enables multiplexing between one audio stream
+and one video stream. These APIs serve as a counterpart to the {@link android.media.MediaExtractor}
 class added in Android 4.2 for de-multiplexing (demuxing) media.</p>
 
-<p>Supported output formats are defined in {@link android.media.MediaMuxer.OutputFormat}. Currently, 
-MP4 is the only supported output format and {@link android.media.MediaMuxer} currently supports 
+<p>Supported output formats are defined in {@link android.media.MediaMuxer.OutputFormat}. Currently,
+MP4 is the only supported output format and {@link android.media.MediaMuxer} currently supports
 only one audio stream and/or one video stream at a time.</p>
 
-<p>{@link android.media.MediaMuxer} is mostly designed to work with {@link android.media.MediaCodec} 
-so you can perform video processing through {@link android.media.MediaCodec} then save the 
-output to an MP4 file through {@link android.media.MediaMuxer}. You can also use {@link 
-android.media.MediaMuxer} in combination with {@link android.media.MediaExtractor} to perform 
+<p>{@link android.media.MediaMuxer} is mostly designed to work with {@link android.media.MediaCodec}
+so you can perform video processing through {@link android.media.MediaCodec} then save the
+output to an MP4 file through {@link android.media.MediaMuxer}. You can also use {@link
+android.media.MediaMuxer} in combination with {@link android.media.MediaExtractor} to perform
 media editing without the need to encode or decode.</p>
 
 
 <h3 id="ProgressAndScrubbing">Playback progress and scrubbing for RemoteControlClient</h3>
 
-<p>In Android 4.0 (API level 14), the {@link android.media.RemoteControlClient} was added to 
-enable media playback controls from remote control clients such as the controls available on the 
-lock screen. Android 4.3 now provides the ability for such controllers to display the playback 
-position and controls for scrubbing the playback. If you've enabled remote control for your 
-media app with the {@link android.media.RemoteControlClient} APIs, then you can allow playback 
+<p>In Android 4.0 (API level 14), the {@link android.media.RemoteControlClient} was added to
+enable media playback controls from remote control clients such as the controls available on the
+lock screen. Android 4.3 now provides the ability for such controllers to display the playback
+position and controls for scrubbing the playback. If you've enabled remote control for your
+media app with the {@link android.media.RemoteControlClient} APIs, then you can allow playback
 scrubbing by implementing two new interfaces.</p>
 
-<p>First, you must enable the {@link 
-android.media.RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE} flag by passing it to 
-{@link android.media.RemoteControlClient#setTransportControlFlags setTransportControlsFlags()}.</p> 
+<p>First, you must enable the {@link
+android.media.RemoteControlClient#FLAG_KEY_MEDIA_POSITION_UPDATE} flag by passing it to
+{@link android.media.RemoteControlClient#setTransportControlFlags setTransportControlsFlags()}.</p>
 
 <p>Then implement the following two new interfaces:</p>
 <dl>
   <dt>{@link android.media.RemoteControlClient.OnGetPlaybackPositionListener}</dt>
-  <dd>This includes the callback {@link android.media.RemoteControlClient.OnGetPlaybackPositionListener#onGetPlaybackPosition}, which requests the current position 
+  <dd>This includes the callback {@link android.media.RemoteControlClient.OnGetPlaybackPositionListener#onGetPlaybackPosition}, which requests the current position
   of your media when the remote control needs to update the progress in its UI.</dd>
 
   <dt>{@link android.media.RemoteControlClient.OnPlaybackPositionUpdateListener}</dt>
-  <dd>This includes the callback {@link android.media.RemoteControlClient.OnPlaybackPositionUpdateListener#onPlaybackPositionUpdate onPlaybackPositionUpdate()}, which 
-  tells your app the new time code for your media when the user scrubs the playback with the 
+  <dd>This includes the callback {@link android.media.RemoteControlClient.OnPlaybackPositionUpdateListener#onPlaybackPositionUpdate onPlaybackPositionUpdate()}, which
+  tells your app the new time code for your media when the user scrubs the playback with the
   remote control UI.
-    <p>Once you update your playback with the new position, call {@link 
-    android.media.RemoteControlClient#setPlaybackState setPlaybackState()} to indicate the 
+    <p>Once you update your playback with the new position, call {@link
+    android.media.RemoteControlClient#setPlaybackState setPlaybackState()} to indicate the
     new playback state, position, and speed.</p>
   </dd>
 </dl>
 
-<p>With these interfaces defined, you can set them for your {@link 
-android.media.RemoteControlClient} by calling {@link android.media.RemoteControlClient#setOnGetPlaybackPositionListener setOnGetPlaybackPositionListener()} and 
-{@link android.media.RemoteControlClient#setPlaybackPositionUpdateListener 
+<p>With these interfaces defined, you can set them for your {@link
+android.media.RemoteControlClient} by calling {@link android.media.RemoteControlClient#setOnGetPlaybackPositionListener setOnGetPlaybackPositionListener()} and
+{@link android.media.RemoteControlClient#setPlaybackPositionUpdateListener
 setPlaybackPositionUpdateListener()}, respectively.</p>
 
 
@@ -560,7 +563,7 @@
 
 <h3 id="OpenGL">Support for OpenGL ES 3.0</h3>
 
-<p>Android 4.3 adds Java interfaces and native support for OpenGL ES 3.0. Key new functionality 
+<p>Android 4.3 adds Java interfaces and native support for OpenGL ES 3.0. Key new functionality
 provided in OpenGL ES 3.0 includes:</p>
 <ul>
   <li>Acceleration of advanced visual effects</li>
@@ -570,8 +573,8 @@
   <li>Broader standardization of texture size and render-buffer formats</li>
 </ul>
 
-<p>The Java interface for OpenGL ES 3.0 on Android is provided with {@link android.opengl.GLES30}. 
-When using OpenGL ES 3.0, be sure that you declare it in your manifest file with the 
+<p>The Java interface for OpenGL ES 3.0 on Android is provided with {@link android.opengl.GLES30}.
+When using OpenGL ES 3.0, be sure that you declare it in your manifest file with the
 <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">&lt;uses-feature></a>
 tag and the {@code android:glEsVersion} attribute. For example:</p>
 <pre>
@@ -586,16 +589,16 @@
 
 <h3 id="MipMap">Mipmapping for drawables</h3>
 
-<p>Using a mipmap as the source for your bitmap or drawable is a simple way to provide a 
-quality image and various image scales, which can be particularly useful if you expect your 
+<p>Using a mipmap as the source for your bitmap or drawable is a simple way to provide a
+quality image and various image scales, which can be particularly useful if you expect your
 image to be scaled during an animation.</p>
 
-<p>Android 4.2 (API level 17) added support for mipmaps in the {@link android.graphics.Bitmap} 
-class&mdash;Android swaps the mip images in your {@link android.graphics.Bitmap} when you've 
-supplied a mipmap source and have enabled {@link android.graphics.Bitmap#setHasMipMap 
-setHasMipMap()}. Now in Android 4.3, you can enable mipmaps for a {@link 
-android.graphics.drawable.BitmapDrawable} object as well, by providing a mipmap asset and 
-setting the {@code android:mipMap} attribute in a bitmap resource file or by calling {@link 
+<p>Android 4.2 (API level 17) added support for mipmaps in the {@link android.graphics.Bitmap}
+class&mdash;Android swaps the mip images in your {@link android.graphics.Bitmap} when you've
+supplied a mipmap source and have enabled {@link android.graphics.Bitmap#setHasMipMap
+setHasMipMap()}. Now in Android 4.3, you can enable mipmaps for a {@link
+android.graphics.drawable.BitmapDrawable} object as well, by providing a mipmap asset and
+setting the {@code android:mipMap} attribute in a bitmap resource file or by calling {@link
 android.graphics.drawable.BitmapDrawable#hasMipMap hasMipMap()}.
 </p>
 
@@ -605,36 +608,36 @@
 
 <h3 id="ViewOverlay">View overlays</h3>
 
-<p>The new {@link android.view.ViewOverlay} class provides a transparent layer on top of 
-a {@link android.view.View} on which you can add visual content and which does not affect 
-the layout hierarchy. You can get a {@link android.view.ViewOverlay} for any {@link 
-android.view.View} by calling {@link android.view.View#getOverlay}. The overlay 
-always has the same size and position as its host view (the view from which it was created), 
-allowing you to add content that appears in front of the host view, but which cannot extend 
+<p>The new {@link android.view.ViewOverlay} class provides a transparent layer on top of
+a {@link android.view.View} on which you can add visual content and which does not affect
+the layout hierarchy. You can get a {@link android.view.ViewOverlay} for any {@link
+android.view.View} by calling {@link android.view.View#getOverlay}. The overlay
+always has the same size and position as its host view (the view from which it was created),
+allowing you to add content that appears in front of the host view, but which cannot extend
 the bounds of that host view.
 </p>
 
-<p>Using a {@link android.view.ViewOverlay} is particularly useful when you want to create 
-animations such as sliding a view outside of its container or moving items around the screen 
-without affecting the view hierarchy. However, because the usable area of an overlay is 
-restricted to the same area as its host view, if you want to animate a view moving outside 
-its position in the layout, you must use an overlay from a parent view that has the desired 
+<p>Using a {@link android.view.ViewOverlay} is particularly useful when you want to create
+animations such as sliding a view outside of its container or moving items around the screen
+without affecting the view hierarchy. However, because the usable area of an overlay is
+restricted to the same area as its host view, if you want to animate a view moving outside
+its position in the layout, you must use an overlay from a parent view that has the desired
 layout bounds.</p>
 
-<p>When you create an overlay for a widget view such as a {@link android.widget.Button}, you 
-can add {@link android.graphics.drawable.Drawable} objects to the overlay by calling 
-{@link android.view.ViewOverlay#add(Drawable)}. If you call {@link 
+<p>When you create an overlay for a widget view such as a {@link android.widget.Button}, you
+can add {@link android.graphics.drawable.Drawable} objects to the overlay by calling
+{@link android.view.ViewOverlay#add(Drawable)}. If you call {@link
 android.view.ViewGroup#getOverlay} for a layout view, such as {@link android.widget.RelativeLayout},
 the object returned is a {@link android.view.ViewGroupOverlay}. The
-{@link android.view.ViewGroupOverlay} class is a subclass 
-of {@link android.view.ViewOverlay} that  also allows you to add {@link android.view.View} 
+{@link android.view.ViewGroupOverlay} class is a subclass
+of {@link android.view.ViewOverlay} that  also allows you to add {@link android.view.View}
 objects by calling {@link android.view.ViewGroupOverlay#add(View)}.
 </p>
 
-<p class="note"><strong>Note:</strong> All drawables and views that you add to an overlay 
+<p class="note"><strong>Note:</strong> All drawables and views that you add to an overlay
 are visual only. They cannot receive focus or input events.</p>
 
-<p>For example, the following code animates a view sliding to the right by placing the view 
+<p>For example, the following code animates a view sliding to the right by placing the view
 in the parent view's overlay, then performing a translation animation on that view:</p>
 <pre>
 View view = findViewById(R.id.view_to_remove);
@@ -647,17 +650,17 @@
 
 <h3 id="OpticalBounds">Optical bounds layout</h3>
 
-<p>For views that contain nine-patch background images, you can now specify that they should 
-be aligned with neighboring views based on the "optical" bounds of the background image rather 
+<p>For views that contain nine-patch background images, you can now specify that they should
+be aligned with neighboring views based on the "optical" bounds of the background image rather
 than the "clip" bounds of the view.</p>
 
-<p>For example, figures 1 and 2 each show the same layout, but the version in figure 1 is 
-using clip bounds (the default behavior), while figure 2 is using optical bounds. Because the 
-nine-patch images used for the button and the photo frame include padding around the edges, 
+<p>For example, figures 1 and 2 each show the same layout, but the version in figure 1 is
+using clip bounds (the default behavior), while figure 2 is using optical bounds. Because the
+nine-patch images used for the button and the photo frame include padding around the edges,
 they don’t appear to align with each other or the text when using clip bounds.</p>
 
-<p class="note"><strong>Note:</strong> The screenshot in figures 1 and 2 have the "Show 
-layout bounds" developer setting enabled. For each view, red lines indicate the optical 
+<p class="note"><strong>Note:</strong> The screenshot in figures 1 and 2 have the "Show
+layout bounds" developer setting enabled. For each view, red lines indicate the optical
 bounds, blue lines indicate the clip bounds, and pink indicates margins.</p>
 
 <script type="text/javascript">
@@ -725,30 +728,30 @@
 </p>
 </div>
 
-<p>For this to work, the nine-patch images applied to the background of your views must specify 
-the optical bounds using red lines along the bottom and right-side of the nine-patch file (as 
-shown in figure 3). The red lines indicate the region that should be subtracted from 
+<p>For this to work, the nine-patch images applied to the background of your views must specify
+the optical bounds using red lines along the bottom and right-side of the nine-patch file (as
+shown in figure 3). The red lines indicate the region that should be subtracted from
 the clip bounds, leaving the optical bounds of the image.</p>
 
-<p>When you enable optical bounds for a {@link android.view.ViewGroup} in your layout, all 
-descendant views inherit the optical bounds layout mode unless you override it for a group by 
-setting {@code android:layoutMode} to {@code "clipBounds"}. All layout elements also honor the 
-optical bounds of their child views, adapting their own bounds based on the optical bounds of 
-the views within them. However, layout elements (subclasses of {@link android.view.ViewGroup}) 
+<p>When you enable optical bounds for a {@link android.view.ViewGroup} in your layout, all
+descendant views inherit the optical bounds layout mode unless you override it for a group by
+setting {@code android:layoutMode} to {@code "clipBounds"}. All layout elements also honor the
+optical bounds of their child views, adapting their own bounds based on the optical bounds of
+the views within them. However, layout elements (subclasses of {@link android.view.ViewGroup})
 currently do not support optical bounds for nine-patch images applied to their own background.</p>
 
 <p>If you create a custom view by subclassing {@link android.view.View}, {@link android.view.ViewGroup}, or any subclasses thereof, your view will inherit these optical bound behaviors.</p>
 
 <p class="note"><strong>Note:</strong> All widgets supported by the Holo theme have been updated
-with optical bounds, including {@link android.widget.Button},  {@link android.widget.Spinner}, 
+with optical bounds, including {@link android.widget.Button},  {@link android.widget.Spinner},
 {@link android.widget.EditText}, and others. So you can immediately benefit by setting the
-{@code android:layoutMode} attribute to {@code "opticalBounds"} if your app applies a Holo theme 
-({@link android.R.style#Theme_Holo Theme.Holo}, {@link android.R.style#Theme_Holo_Light 
+{@code android:layoutMode} attribute to {@code "opticalBounds"} if your app applies a Holo theme
+({@link android.R.style#Theme_Holo Theme.Holo}, {@link android.R.style#Theme_Holo_Light
 Theme.Holo.Light}, etc.).
 </p>
 
-<p>To specify optical bounds for your own nine-patch images with the <a 
-href="{@docRoot}tools/help/draw9patch.html">Draw 9-patch</a> tool, hold CTRL when clicking on 
+<p>To specify optical bounds for your own nine-patch images with the <a
+href="{@docRoot}tools/help/draw9patch.html">Draw 9-patch</a> tool, hold CTRL when clicking on
 the border pixels.</p>
 
 
@@ -756,59 +759,59 @@
 
 <h3 id="AnimationRect">Animation for Rect values</h3>
 
-<p>You can now animate between two {@link android.graphics.Rect} values with the new {@link 
-android.animation.RectEvaluator}. This new class is an implementation of {@link 
-android.animation.TypeEvaluator} that you can pass to {@link 
+<p>You can now animate between two {@link android.graphics.Rect} values with the new {@link
+android.animation.RectEvaluator}. This new class is an implementation of {@link
+android.animation.TypeEvaluator} that you can pass to {@link
 android.animation.ValueAnimator#setEvaluator ValueAnimator.setEvaluator()}.
 </p>
 
 <h3 id="AttachFocus">Window attach and focus listener</h3>
 
-<p>Previously, if you wanted to listen for when your view attached/detached to the window or 
-when its focus changed, you needed to override the {@link android.view.View} class to 
-implement {@link android.view.View#onAttachedToWindow onAttachedToWindow()} and {@link 
-android.view.View#onDetachedFromWindow onDetachedFromWindow()}, or  {@link 
+<p>Previously, if you wanted to listen for when your view attached/detached to the window or
+when its focus changed, you needed to override the {@link android.view.View} class to
+implement {@link android.view.View#onAttachedToWindow onAttachedToWindow()} and {@link
+android.view.View#onDetachedFromWindow onDetachedFromWindow()}, or  {@link
 android.view.View#onWindowFocusChanged onWindowFocusChanged()}, respectively.
 </p>
 
-<p>Now, to receive attach and detach events you can instead implement {@link 
-android.view.ViewTreeObserver.OnWindowAttachListener} and set it on a view with 
-{@link android.view.ViewTreeObserver#addOnWindowAttachListener addOnWindowAttachListener()}. 
-And to receive focus events, you can implement {@link 
-android.view.ViewTreeObserver.OnWindowFocusChangeListener} and set it on a view with 
-{@link android.view.ViewTreeObserver#addOnWindowFocusChangeListener 
+<p>Now, to receive attach and detach events you can instead implement {@link
+android.view.ViewTreeObserver.OnWindowAttachListener} and set it on a view with
+{@link android.view.ViewTreeObserver#addOnWindowAttachListener addOnWindowAttachListener()}.
+And to receive focus events, you can implement {@link
+android.view.ViewTreeObserver.OnWindowFocusChangeListener} and set it on a view with
+{@link android.view.ViewTreeObserver#addOnWindowFocusChangeListener
 addOnWindowFocusChangeListener()}.
 </p>
 
 
 <h3 id="Overscan">TV overscan support</h3>
 
-<p>To be sure your app fills the entire screen on every television, you can now enable overscan 
-for you app layout. Overscan mode is determined by the {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag, which you can enable with platform themes such as 
-{@link android.R.style#Theme_DeviceDefault_NoActionBar_Overscan} or by enabling the 
+<p>To be sure your app fills the entire screen on every television, you can now enable overscan
+for you app layout. Overscan mode is determined by the {@link android.view.WindowManager.LayoutParams#FLAG_LAYOUT_IN_OVERSCAN} flag, which you can enable with platform themes such as
+{@link android.R.style#Theme_DeviceDefault_NoActionBar_Overscan} or by enabling the
 {@link android.R.attr#windowOverscan} style in a custom theme.</p>
 
 
 <h3 id="Orientation">Screen orientation</h3>
 
-<p>The <a 
+<p>The <a
 href="{@docRoot}guide/topics/manifest/activity-element.html">{@code &lt;activity>}</a>
-tag's <a 
+tag's <a
 href="{@docRoot}guide/topics/manifest/activity-element.html#screen">{@code screenOrientation}</a>
 attribute now supports additional values to honor the user's preference for auto-rotation:</p>
 <dl>
 <dt>{@code "userLandscape"}</dt>
-<dd>Behaves the same as {@code "sensorLandscape"}, except if the user disables auto-rotate 
+<dd>Behaves the same as {@code "sensorLandscape"}, except if the user disables auto-rotate
 then it locks in the normal landscape orientation and will not flip.
 </dd>
 
 <dt>{@code "userPortrait"}</dt>
-<dd>Behaves the same as {@code "sensorPortrait"}, except if the user disables auto-rotate then 
+<dd>Behaves the same as {@code "sensorPortrait"}, except if the user disables auto-rotate then
 it locks in the normal portrait orientation and will not flip.
 </dd>
 
 <dt>{@code "fullUser"}</dt>
-<dd>Behaves the same as {@code "fullSensor"} and allows rotation in all four directions, except 
+<dd>Behaves the same as {@code "fullSensor"} and allows rotation in all four directions, except
 if the user disables auto-rotate then it locks in the user's preferred orientation.
 </dd></dl>
 
@@ -818,8 +821,8 @@
 
 <h3 id="RotationAnimation">Rotation animations</h3>
 
-<p>The new {@link android.view.WindowManager.LayoutParams#rotationAnimation} field in 
-{@link android.view.WindowManager} allows you to select between one of three animations you 
+<p>The new {@link android.view.WindowManager.LayoutParams#rotationAnimation} field in
+{@link android.view.WindowManager} allows you to select between one of three animations you
 want to use when the system switches screen orientations. The three animations are:</p>
 <ul>
   <li>{@link android.view.WindowManager.LayoutParams#ROTATION_ANIMATION_CROSSFADE}</li>
@@ -844,25 +847,17 @@
 
 <h2 id="UserInput">User Input</h2>
 
-<h3 id="SignificantMotion">Detect significant motion</h3>
-
-<p>The {@link android.hardware.SensorManager} APIs now allow you to request a callback when the 
-device sensors detect "significant motion." For instance, this event may be triggered by new 
-motion such as when the user starts to walk.</p>
-
-<p>To register a listener for significant motion, extend the {@link android.hardware.TriggerEventListener} class and implement the {@link android.hardware.TriggerEventListener#onTrigger onTrigger()} callback method. Then register your event listener with the {@link android.hardware.SensorManager} by passing it to {@link android.hardware.SensorManager#requestTriggerSensor requestTriggerSensor()}, passing it your {@link android.hardware.TriggerEventListener} and {@link android.hardware.Sensor#TYPE_SIGNIFICANT_MOTION}.</p>
-
 <h3 id="Sensors">New sensor types</h3>
 <p>The new {@link android.hardware.Sensor#TYPE_GAME_ROTATION_VECTOR} sensor allows you to detect the device's rotations without worrying about magnetic interferences. Unlike the {@link android.hardware.Sensor#TYPE_ROTATION_VECTOR} sensor, the {@link android.hardware.Sensor#TYPE_GAME_ROTATION_VECTOR} is not based on magnetic north.</p>
 
-<p>The new {@link android.hardware.Sensor#TYPE_GYROSCOPE_UNCALIBRATED} and {@link 
-android.hardware.Sensor#TYPE_MAGNETIC_FIELD_UNCALIBRATED} sensors provide raw sensor data without 
-consideration for bias estimations. That is, the existing {@link 
-android.hardware.Sensor#TYPE_GYROSCOPE} and {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD} 
-sensors provide sensor data that takes into account estimated bias from gyro-drift and hard iron 
-in the device, respectively. Whereas the new "uncalibrated" versions of these sensors instead provide 
-the raw sensor data and offer the estimated bias values separately. These sensors allow you to 
-provide your own custom calibration for the sensor data by enhancing the estimated bias with 
+<p>The new {@link android.hardware.Sensor#TYPE_GYROSCOPE_UNCALIBRATED} and {@link
+android.hardware.Sensor#TYPE_MAGNETIC_FIELD_UNCALIBRATED} sensors provide raw sensor data without
+consideration for bias estimations. That is, the existing {@link
+android.hardware.Sensor#TYPE_GYROSCOPE} and {@link android.hardware.Sensor#TYPE_MAGNETIC_FIELD}
+sensors provide sensor data that takes into account estimated bias from gyro-drift and hard iron
+in the device, respectively. Whereas the new "uncalibrated" versions of these sensors instead provide
+the raw sensor data and offer the estimated bias values separately. These sensors allow you to
+provide your own custom calibration for the sensor data by enhancing the estimated bias with
 external data.</p>
 
 
@@ -891,13 +886,13 @@
 
 <p>To track which contacts have been deleted, the new table {@link android.provider.ContactsContract.DeletedContacts} provides a log of contacts that have been deleted (but each contact deleted is held in this table for a limited time). Similar to {@link android.provider.ContactsContract.ContactsColumns#CONTACT_LAST_UPDATED_TIMESTAMP}, you can use the new selection parameter, {@link android.provider.ContactsContract.DeletedContacts#CONTACT_DELETED_TIMESTAMP} to check which contacts have been deleted since the last time you queried the provider. The table also contains the constant {@link android.provider.ContactsContract.DeletedContacts#DAYS_KEPT_MILLISECONDS} containing the number of days (in milliseconds) that the log will be kept.</p>
 
-<p>Additionally, the Contacts Provider now broadcasts the {@link 
-android.provider.ContactsContract.Intents#CONTACTS_DATABASE_CREATED} action when the user 
-clears the contacts storage through the system settings menu, effectively recreating the 
-Contacts Provider database. It’s intended to signal apps that they need to drop all the contact 
+<p>Additionally, the Contacts Provider now broadcasts the {@link
+android.provider.ContactsContract.Intents#CONTACTS_DATABASE_CREATED} action when the user
+clears the contacts storage through the system settings menu, effectively recreating the
+Contacts Provider database. It’s intended to signal apps that they need to drop all the contact
 information they’ve stored and reload it with a new query.</p>
 
-<p>For sample code using these APIs to check for changes to the contacts, look in the ApiDemos 
+<p>For sample code using these APIs to check for changes to the contacts, look in the ApiDemos
 sample available in the <a href="{@docRoot}tools/samples/index.html">SDK Samples</a> download.</p>
 
 
@@ -905,13 +900,13 @@
 
 <h3 id="BiDi">Improved support for bi-directional text</h3>
 
-<p>Previous versions of Android support right-to-left (RTL) languages and layout, 
-but sometimes don't properly handle mixed-direction text. So Android 4.3 adds the {@link 
-android.text.BidiFormatter} APIs that help you properly format text with opposite-direction 
+<p>Previous versions of Android support right-to-left (RTL) languages and layout,
+but sometimes don't properly handle mixed-direction text. So Android 4.3 adds the {@link
+android.text.BidiFormatter} APIs that help you properly format text with opposite-direction
 content without garbling any parts of it.</p>
 
-<p>For example, when you want to create a sentence with a string variable, such as "Did you mean 
-15 Bay Street, Laurel, CA?", you normally pass a localized string resource and the variable to 
+<p>For example, when you want to create a sentence with a string variable, such as "Did you mean
+15 Bay Street, Laurel, CA?", you normally pass a localized string resource and the variable to
 {@link java.lang.String#format String.format()}:</p>
 <pre>
 Resources res = getResources();
@@ -922,8 +917,8 @@
 
 <p dir="rtl">האם התכוונת ל 15 Bay Street, Laurel, CA?</p>
 
-<p>That's wrong because the "15" should be left of "Bay Street." The solution is to use {@link 
-android.text.BidiFormatter} and its {@link android.text.BidiFormatter#unicodeWrap(String) 
+<p>That's wrong because the "15" should be left of "Bay Street." The solution is to use {@link
+android.text.BidiFormatter} and its {@link android.text.BidiFormatter#unicodeWrap(String)
 unicodeWrap()} method. For example, the code above becomes:</p>
 <pre>
 Resources res = getResources();
@@ -933,11 +928,11 @@
 </pre>
 
 <p>
-By default, {@link android.text.BidiFormatter#unicodeWrap(String) unicodeWrap()} uses the 
-first-strong directionality estimation heuristic, which can get things wrong if the first 
-signal for text direction does not represent the appropriate direction for the content as a whole. 
-If necessary, you can specify a different heuristic by passing one of the {@link 
-android.text.TextDirectionHeuristic} constants from {@link android.text.TextDirectionHeuristics} 
+By default, {@link android.text.BidiFormatter#unicodeWrap(String) unicodeWrap()} uses the
+first-strong directionality estimation heuristic, which can get things wrong if the first
+signal for text direction does not represent the appropriate direction for the content as a whole.
+If necessary, you can specify a different heuristic by passing one of the {@link
+android.text.TextDirectionHeuristic} constants from {@link android.text.TextDirectionHeuristics}
 to {@link android.text.BidiFormatter#unicodeWrap(String,TextDirectionHeuristic) unicodeWrap()}.</p>
 
 <p class="note"><strong>Note:</strong> These new APIs are also available for previous versions
@@ -950,31 +945,31 @@
 
 <h3 id="A11yKeyEvents">Handle key events</h3>
 
-<p>An {@link android.accessibilityservice.AccessibilityService} can now receive a callback for 
-key input events with the {@link android.accessibilityservice.AccessibilityService#onKeyEvent 
-onKeyEvent()} callback method. This allows your accessibility service to handle input for 
-key-based input devices such as a keyboard and translate those events to special actions that 
+<p>An {@link android.accessibilityservice.AccessibilityService} can now receive a callback for
+key input events with the {@link android.accessibilityservice.AccessibilityService#onKeyEvent
+onKeyEvent()} callback method. This allows your accessibility service to handle input for
+key-based input devices such as a keyboard and translate those events to special actions that
 previously may have been possible only with touch input or the device's directional pad.</p>
 
 
 <h3 id="A11yText">Select text and copy/paste</h3>
 
-<p>The {@link android.view.accessibility.AccessibilityNodeInfo} now provides APIs that allow 
-an {@link android.accessibilityservice.AccessibilityService} to select, cut, copy, and paste 
+<p>The {@link android.view.accessibility.AccessibilityNodeInfo} now provides APIs that allow
+an {@link android.accessibilityservice.AccessibilityService} to select, cut, copy, and paste
 text in a node.</p>
 
-<p>To specify the selection of text to cut or copy, your accessibility service can use the new 
-action, {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_SET_SELECTION}, passing 
-with it the selection start and end position with {@link 
-android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT} and {@link 
-android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT}. 
-Alternatively you can select text by manipulating the cursor position using the existing 
-action, {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY} 
-(previously only for moving the cursor position), and adding the argument {@link 
+<p>To specify the selection of text to cut or copy, your accessibility service can use the new
+action, {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_SET_SELECTION}, passing
+with it the selection start and end position with {@link
+android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT} and {@link
+android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT}.
+Alternatively you can select text by manipulating the cursor position using the existing
+action, {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}
+(previously only for moving the cursor position), and adding the argument {@link
 android.view.accessibility.AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}.</p>
 
-<p>You can then cut or copy with {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_CUT}, 
-{@link android.view.accessibility.AccessibilityNodeInfo#ACTION_COPY}, then later paste with 
+<p>You can then cut or copy with {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_CUT},
+{@link android.view.accessibility.AccessibilityNodeInfo#ACTION_COPY}, then later paste with
 {@link android.view.accessibility.AccessibilityNodeInfo#ACTION_PASTE}.</p>
 
 
@@ -987,14 +982,14 @@
 
 <h3 id="A11yFeatures">Declare accessibility features</h3>
 
-<p>Beginning with Android 4.3, an accessibility service must declare accessibility capabilities 
-in its metadata file in order to use certain accessibility features. If the capability is not 
-requested in the metadata file, then the feature will be a no-op. To declare your service's 
-accessibility capabilities, you must use XML attributes that correspond to the various 
-"capability" constants in the {@link android.accessibilityservice.AccessibilityServiceInfo} 
+<p>Beginning with Android 4.3, an accessibility service must declare accessibility capabilities
+in its metadata file in order to use certain accessibility features. If the capability is not
+requested in the metadata file, then the feature will be a no-op. To declare your service's
+accessibility capabilities, you must use XML attributes that correspond to the various
+"capability" constants in the {@link android.accessibilityservice.AccessibilityServiceInfo}
 class.</p>
 
-<p>For example, if a service does not request the {@link android.R.styleable#AccessibilityService_canRequestFilterKeyEvents flagRequestFilterKeyEvents} capability, 
+<p>For example, if a service does not request the {@link android.R.styleable#AccessibilityService_canRequestFilterKeyEvents flagRequestFilterKeyEvents} capability,
 then it will not receive key events.</p>
 
 
@@ -1002,36 +997,36 @@
 
 <h3 id="UiAutomation">Automated UI testing</h3>
 
-<p>The new {@link android.app.UiAutomation} class provides APIs that allow you to simulate user 
-actions for test automation. By using the platform's {@link 
-android.accessibilityservice.AccessibilityService} APIs, the {@link android.app.UiAutomation} 
+<p>The new {@link android.app.UiAutomation} class provides APIs that allow you to simulate user
+actions for test automation. By using the platform's {@link
+android.accessibilityservice.AccessibilityService} APIs, the {@link android.app.UiAutomation}
 APIs allow you to inspect the screen content and inject arbitrary keyboard and touch events.</p>
 
-<p>To get an instance of {@link android.app.UiAutomation}, call {@link 
-android.app.Instrumentation#getUiAutomation Instrumentation.getUiAutomation()}. In order 
-for this to work, you must supply the {@code -w} option with the {@code instrument} command 
-when running your {@link android.test.InstrumentationTestCase} from <a 
+<p>To get an instance of {@link android.app.UiAutomation}, call {@link
+android.app.Instrumentation#getUiAutomation Instrumentation.getUiAutomation()}. In order
+for this to work, you must supply the {@code -w} option with the {@code instrument} command
+when running your {@link android.test.InstrumentationTestCase} from <a
 href="{@docRoot}tools/help/adb.html#am">{@code adb shell}</a>.</p>
 
-<p>With the {@link android.app.UiAutomation} instance, you can execute arbitrary events to test 
-your app by calling {@link android.app.UiAutomation#executeAndWaitForEvent 
-executeAndWaitForEvent()}, passing it a {@link java.lang.Runnable} to perform, a timeout 
-period for the operation, and an implementation of the {@link 
-android.app.UiAutomation.AccessibilityEventFilter} interface. It's within your {@link 
-android.app.UiAutomation.AccessibilityEventFilter} implementation that you'll receive a call 
-that allows you to filter the events that you're interested in and determine the success or 
+<p>With the {@link android.app.UiAutomation} instance, you can execute arbitrary events to test
+your app by calling {@link android.app.UiAutomation#executeAndWaitForEvent
+executeAndWaitForEvent()}, passing it a {@link java.lang.Runnable} to perform, a timeout
+period for the operation, and an implementation of the {@link
+android.app.UiAutomation.AccessibilityEventFilter} interface. It's within your {@link
+android.app.UiAutomation.AccessibilityEventFilter} implementation that you'll receive a call
+that allows you to filter the events that you're interested in and determine the success or
 failure of a given test case.</p>
 
-<p>To observe all the events during a test, create an implementation of {@link 
-android.app.UiAutomation.OnAccessibilityEventListener} and pass it to {@link 
-android.app.UiAutomation#setOnAccessibilityEventListener setOnAccessibilityEventListener()}.  
-Your listener interface then receives a call to {@link 
-android.app.UiAutomation.OnAccessibilityEventListener#onAccessibilityEvent onAccessibilityEvent()} 
-each time an event occurs, receiving an {@link android.view.accessibility.AccessibilityEvent} object 
+<p>To observe all the events during a test, create an implementation of {@link
+android.app.UiAutomation.OnAccessibilityEventListener} and pass it to {@link
+android.app.UiAutomation#setOnAccessibilityEventListener setOnAccessibilityEventListener()}.
+Your listener interface then receives a call to {@link
+android.app.UiAutomation.OnAccessibilityEventListener#onAccessibilityEvent onAccessibilityEvent()}
+each time an event occurs, receiving an {@link android.view.accessibility.AccessibilityEvent} object
 that describes the event.</p>
 
-<p>There is a variety of other operations that the {@link android.app.UiAutomation} APIs expose 
-at a very low level to encourage the development of UI test tools such as <a href="{@docRoot}tools/help/uiautomator/index.html">uiautomator</a>. For instance, 
+<p>There is a variety of other operations that the {@link android.app.UiAutomation} APIs expose
+at a very low level to encourage the development of UI test tools such as <a href="{@docRoot}tools/help/uiautomator/index.html">uiautomator</a>. For instance,
 {@link android.app.UiAutomation} can also:</p>
 <ul>
   <li>Inject input events
@@ -1039,16 +1034,16 @@
   <li>Take screenshots
 </ul>
 
-<p>And most importantly for UI test tools, the {@link android.app.UiAutomation} APIs work 
+<p>And most importantly for UI test tools, the {@link android.app.UiAutomation} APIs work
 across application boundaries, unlike those in {@link android.app.Instrumentation}.</p>
 
 
 <h3 id="Systrace">Systrace events for apps</h3>
 
-<p>Android 4.3 adds the {@link android.os.Trace} class with two static methods, 
-{@link android.os.Trace#beginSection beginSection()} and {@link android.os.Trace#endSection()}, 
-which allow you to define blocks of code to include with the systrace report. By creating 
-sections of traceable code in your app, the systrace logs provide you a much more detailed 
+<p>Android 4.3 adds the {@link android.os.Trace} class with two static methods,
+{@link android.os.Trace#beginSection beginSection()} and {@link android.os.Trace#endSection()},
+which allow you to define blocks of code to include with the systrace report. By creating
+sections of traceable code in your app, the systrace logs provide you a much more detailed
 analysis of where slowdown occurs within your app.</p>
 
 <p>For information about using the Systrace tool, read <a href="{@docRoot}tools/debugging/systrace.html">Analyzing Display and Performance with Systrace</a>.</p>
@@ -1058,31 +1053,31 @@
 
 <h3 id="KeyStore">Android key store for app-private keys</h3>
 
-<p>Android now offers a custom Java Security Provider in the {@link java.security.KeyStore} 
-facility, called Android Key Store, which allows you to generate and save private keys that 
-may be seen and used by only your app. To load the Android Key Store, pass 
-{@code "AndroidKeyStore"} to {@link java.security.KeyStore#getInstance(String) 
+<p>Android now offers a custom Java Security Provider in the {@link java.security.KeyStore}
+facility, called Android Key Store, which allows you to generate and save private keys that
+may be seen and used by only your app. To load the Android Key Store, pass
+{@code "AndroidKeyStore"} to {@link java.security.KeyStore#getInstance(String)
 KeyStore.getInstance()}.</p>
 
-<p>To manage your app's private credentials in the Android Key Store, generate a new key with 
-{@link java.security.KeyPairGenerator} with {@link android.security.KeyPairGeneratorSpec}. First 
-get an instance of {@link java.security.KeyPairGenerator} by calling {@link 
-java.security.KeyPairGenerator#getInstance getInstance()}. Then call 
-{@link java.security.KeyPairGenerator#initialize initialize()}, passing it an instance of 
-{@link android.security.KeyPairGeneratorSpec}, which you can get using 
-{@link android.security.KeyPairGeneratorSpec.Builder KeyPairGeneratorSpec.Builder}. 
-Finally, get your {@link java.security.KeyPair} by calling {@link 
+<p>To manage your app's private credentials in the Android Key Store, generate a new key with
+{@link java.security.KeyPairGenerator} with {@link android.security.KeyPairGeneratorSpec}. First
+get an instance of {@link java.security.KeyPairGenerator} by calling {@link
+java.security.KeyPairGenerator#getInstance getInstance()}. Then call
+{@link java.security.KeyPairGenerator#initialize initialize()}, passing it an instance of
+{@link android.security.KeyPairGeneratorSpec}, which you can get using
+{@link android.security.KeyPairGeneratorSpec.Builder KeyPairGeneratorSpec.Builder}.
+Finally, get your {@link java.security.KeyPair} by calling {@link
 java.security.KeyPairGenerator#generateKeyPair generateKeyPair()}.</p>
 
 
 <h3 id="HardwareKeyChain">Hardware credential storage</h3>
 
-<p>Android also now supports hardware-backed storage for your {@link android.security.KeyChain} 
-credentials, providing more security by making the keys unavailable for extraction. That is, once 
-keys are in a hardware-backed key store (Secure Element, TPM, or TrustZone), they can be used for 
-cryptographic operations but the private key material cannot be exported. Even the OS kernel 
-cannot access this key material. While not all Android-powered devices support storage on 
-hardware, you can check at runtime if hardware-backed storage is available by calling 
+<p>Android also now supports hardware-backed storage for your {@link android.security.KeyChain}
+credentials, providing more security by making the keys unavailable for extraction. That is, once
+keys are in a hardware-backed key store (Secure Element, TPM, or TrustZone), they can be used for
+cryptographic operations but the private key material cannot be exported. Even the OS kernel
+cannot access this key material. While not all Android-powered devices support storage on
+hardware, you can check at runtime if hardware-backed storage is available by calling
 {@link android.security.KeyChain#isBoundKeyAlgorithm KeyChain.IsBoundKeyAlgorithm()}.</p>
 
 
@@ -1091,9 +1086,9 @@
 
 <h3 id="ManifestFeatures">Declarable required features</h3>
 
-<p>The following values are now supported in the <a 
+<p>The following values are now supported in the <a
 href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code &lt;uses-feature>}</a>
-element so you can ensure that your app is installed only on devices that provide the features 
+element so you can ensure that your app is installed only on devices that provide the features
 your app needs.</p>
 
 <dl>
@@ -1137,8 +1132,8 @@
 
 
 <h3 id="ManifestPermissions">User permissions</h3>
-<p>The following values are now supported in the <a 
-href="{@docRoot}guide/topics/manifest/uses-permission-element.html">{@code &lt;uses-permission>}</a> 
+<p>The following values are now supported in the <a
+href="{@docRoot}guide/topics/manifest/uses-permission-element.html">{@code &lt;uses-permission>}</a>
 to declare the
 permissions your app requires in order to access certain APIs.</p>
 
diff --git a/docs/html/about/versions/jelly-bean.jd b/docs/html/about/versions/jelly-bean.jd
index 71957be..5812f3d 100644
--- a/docs/html/about/versions/jelly-bean.jd
+++ b/docs/html/about/versions/jelly-bean.jd
@@ -5,7 +5,14 @@
 tab2.link=#android-41
 
 @jd:body
-
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <style>
 #android-41 {display:none;}
diff --git a/docs/html/channels/io2013.jd b/docs/html/channels/io2013.jd
index 977eb2f..b2bde31 100644
--- a/docs/html/channels/io2013.jd
+++ b/docs/html/channels/io2013.jd
@@ -1,7 +1,15 @@
 fullpage=true
 page.title=Google I/O 13
 @jd:body
-    
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
+
 <style>
 #ioplayer-frame {
   z-index:10;
diff --git a/docs/html/design/downloads/index.jd b/docs/html/design/downloads/index.jd
index 00f4467..ab6bb1b 100644
--- a/docs/html/design/downloads/index.jd
+++ b/docs/html/design/downloads/index.jd
@@ -1,5 +1,13 @@
 page.title=Downloads
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <div class="layout-content-row">
   <div class="layout-content-col span-9">
diff --git a/docs/html/design/index.jd b/docs/html/design/index.jd
index 1e6b40c..d4ef07f 100644
--- a/docs/html/design/index.jd
+++ b/docs/html/design/index.jd
@@ -2,6 +2,14 @@
 header.hide=1
 footer.hide=1
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <style>
 #landing-graphic-container {
diff --git a/docs/html/develop/index.jd b/docs/html/develop/index.jd
index 7b5cb4a..f96e868 100644
--- a/docs/html/develop/index.jd
+++ b/docs/html/develop/index.jd
@@ -4,6 +4,14 @@
 carousel=1
 tabbedList=1
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <style>
 #noplayer-message {
diff --git a/docs/html/distribute/googleplay/promote/badges.jd b/docs/html/distribute/googleplay/promote/badges.jd
index 9a32921..93092bf 100644
--- a/docs/html/distribute/googleplay/promote/badges.jd
+++ b/docs/html/distribute/googleplay/promote/badges.jd
@@ -1,5 +1,13 @@
 page.title=Google Play Badges
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <p itemprop="description">Google Play badges allow you to promote your app with official branding
 in your online ads, promotional materials, or anywhere else you want a link to your app.</p>
diff --git a/docs/html/distribute/googleplay/promote/brand.jd b/docs/html/distribute/googleplay/promote/brand.jd
index 265584f..a047b1f 100644
--- a/docs/html/distribute/googleplay/promote/brand.jd
+++ b/docs/html/distribute/googleplay/promote/brand.jd
@@ -1,6 +1,13 @@
 page.title=Brand Guidelines
 @jd:body
-
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 
 <p>We encourage you to use the Android and Google Play brands with your Android app
diff --git a/docs/html/distribute/googleplay/promote/index.jd b/docs/html/distribute/googleplay/promote/index.jd
index 6882990..14f37c4 100644
--- a/docs/html/distribute/googleplay/promote/index.jd
+++ b/docs/html/distribute/googleplay/promote/index.jd
@@ -3,6 +3,14 @@
 header.hide=0
 footer.hide=0
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <!--
 <style>
diff --git a/docs/html/distribute/googleplay/promote/linking.jd b/docs/html/distribute/googleplay/promote/linking.jd
index 4fdc5db..014582a 100644
--- a/docs/html/distribute/googleplay/promote/linking.jd
+++ b/docs/html/distribute/googleplay/promote/linking.jd
@@ -1,5 +1,13 @@
 page.title=Linking to Your Products
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <div class="sidebox-wrapper">
 <div class="sidebox">
diff --git a/docs/html/distribute/googleplay/publish/preparing.jd b/docs/html/distribute/googleplay/publish/preparing.jd
index 5593f4f..dd35b25 100644
--- a/docs/html/distribute/googleplay/publish/preparing.jd
+++ b/docs/html/distribute/googleplay/publish/preparing.jd
@@ -1,6 +1,14 @@
 page.title=Launch Checklist
 page.tags="publishing","launch","Google Play", "Developer Console"
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <div id="qv-wrapper"><div id="qv">
 <h2>Checklist</h2>
diff --git a/docs/html/distribute/googleplay/quality/core.jd b/docs/html/distribute/googleplay/quality/core.jd
index 9e23bcc..3fd221c 100644
--- a/docs/html/distribute/googleplay/quality/core.jd
+++ b/docs/html/distribute/googleplay/quality/core.jd
@@ -1,5 +1,13 @@
 page.title=Core App Quality Guidelines
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <div id="qv-wrapper"><div id="qv">
 <h2>Quality Criteria</h2>
diff --git a/docs/html/distribute/googleplay/quality/index.jd b/docs/html/distribute/googleplay/quality/index.jd
index ef537b1..def42e5 100644
--- a/docs/html/distribute/googleplay/quality/index.jd
+++ b/docs/html/distribute/googleplay/quality/index.jd
@@ -1,5 +1,13 @@
 page.title=App Quality
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <p>App quality directly influences the long-term success of your app&mdash;in
 terms of installs, user rating and reviews, engagement, and user retention.
diff --git a/docs/html/distribute/googleplay/quality/tablet.jd b/docs/html/distribute/googleplay/quality/tablet.jd
index 5a707be..c80c3cc 100644
--- a/docs/html/distribute/googleplay/quality/tablet.jd
+++ b/docs/html/distribute/googleplay/quality/tablet.jd
@@ -1,5 +1,13 @@
 page.title=Tablet App Quality Checklist
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <div id="qv-wrapper"><div id="qv">
 <h2>Checklist</h2>
diff --git a/docs/html/distribute/googleplay/spotlight/index.jd b/docs/html/distribute/googleplay/spotlight/index.jd
index b83080e..7004b0a 100644
--- a/docs/html/distribute/googleplay/spotlight/index.jd
+++ b/docs/html/distribute/googleplay/spotlight/index.jd
@@ -3,7 +3,14 @@
 header.hide=0
 
 @jd:body
-
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <p>Android developers, their apps, and their successes with Android and Google Play. </p>
 
@@ -15,6 +22,35 @@
             margin-bottom:40px;
             margin-top:30px;">
    <div style="padding:0 0 0 29px;">
+        <h4>Developer Story: Colopl</h4>
+          <img alt="" class="screenshot thumbnail" style="-webkit-border-radius: 5px;
+            -moz-border-radius: 5px;
+            border-radius: 5px height:78px;
+            width: 78px;
+            float: left;
+            margin: 17px 20px 9px 0;" 
+            src="//lh3.ggpht.com/sx2ILNaXQYOsHfR91T5tUWGlfXE1FutHCBN02Fll6mi9gIaG6RZCGbeJMtIvOoegCPTh=w124" >
+          <div style="width:700px;">
+          <p style="margin-top:26px;
+                    margin-bottom:12px;">
+          The creators of Kuma The Bear, Japan-based <a href="https://play.google.com/store/apps/developer?id=COLOPL,+Inc." target="_android">Colopl</a>, talk about how Google Play and Android allowed them to grow their business to become one of the most profitable games publishers in APAC to date. </p>
+           </div>
+           <iframe style="float:left;
+             margin-right:24px;
+             margin-top:14px;" width="700" height="394" src=
+             "http://www.youtube.com/embed/CbpoZeQCNe4?HD=1;rel=0;origin=developer.android.com;" frameborder="0" allowfullscreen>
+           </iframe>
+   </div> 
+</div>
+
+<div style="background: #F0F0F0;
+            border-top: 1px solid #DDD;
+            padding: 0px 0 24px 0;
+            overflow: auto;
+            clear:both;
+            margin-bottom:40px;
+            margin-top:30px;">
+   <div style="padding:0 0 0 29px;">
         <h4>Developer Story: redBus.in</h4>
           <img alt="" class="screenshot thumbnail" style="-webkit-border-radius: 5px;
             -moz-border-radius: 5px;
diff --git a/docs/html/distribute/index.jd b/docs/html/distribute/index.jd
index 54f9301..8e7c6e1 100644
--- a/docs/html/distribute/index.jd
+++ b/docs/html/distribute/index.jd
@@ -2,6 +2,14 @@
 header.hide=1
 
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
     
     
 <div class="marquee">
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png b/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png
index 697fb7d..cc5b1af 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/land_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png
index 735262f..2625edb 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/land_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png
index cfb7952..9d91475 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/land_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png b/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png
index 5bb815a..f54a8af 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/port_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png
index 1be3b21..230bad4 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/port_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png
index 7e8aff2..2401d20 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/port_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png b/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png
index b5db82e..57b4c03 100644
--- a/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7/thumb.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_back.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_back.png
new file mode 100644
index 0000000..697fb7d
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_fore.png
new file mode 100644
index 0000000..735262f
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_shadow.png
new file mode 100644
index 0000000..cfb7952
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/land_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_back.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_back.png
new file mode 100644
index 0000000..5bb815a
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_back.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_fore.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_fore.png
new file mode 100644
index 0000000..1be3b21
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_fore.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_shadow.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_shadow.png
new file mode 100644
index 0000000..7e8aff2
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/port_shadow.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art-resources/nexus_7_2012/thumb.png b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/thumb.png
new file mode 100644
index 0000000..b5db82e
--- /dev/null
+++ b/docs/html/distribute/promote/device-art-resources/nexus_7_2012/thumb.png
Binary files differ
diff --git a/docs/html/distribute/promote/device-art.jd b/docs/html/distribute/promote/device-art.jd
index 55b846e..09a3941 100644
--- a/docs/html/distribute/promote/device-art.jd
+++ b/docs/html/distribute/promote/device-art.jd
@@ -1,5 +1,13 @@
 page.title=Device Art Generator
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <p>The device art generator allows you to quickly wrap your app screenshots in real device artwork.
 This provides better visual context for your app screenshots on your web site or in other
@@ -165,12 +173,13 @@
       title: 'Nexus 7',
       url: 'http://www.google.com/nexus/7/',
       physicalSize: 7,
-      physicalHeight: 7.81,
-      density: '213dpi',
+      physicalHeight: 8,
+      actualResolution: [1200,1920],
+      density: 'XHDPI',
       landRes: ['shadow', 'back', 'fore'],
-      landOffset: [315,270],
+      landOffset: [326,245],
       portRes: ['shadow', 'back', 'fore'],
-      portOffset: [264,311],
+      portOffset: [244,326],
       portSize: [800,1280]
     },
     {
@@ -202,6 +211,20 @@
       archived: true
     },
     {
+      id: 'nexus_7_2012',
+      title: 'Nexus 7 (2012)',
+      url: 'http://www.google.com/nexus/7/',
+      physicalSize: 7,
+      physicalHeight: 7.81,
+      density: '213dpi',
+      landRes: ['shadow', 'back', 'fore'],
+      landOffset: [315,270],
+      portRes: ['shadow', 'back', 'fore'],
+      portOffset: [264,311],
+      portSize: [800,1280],
+      archived: true
+    },
+    {
       id: 'galaxy_nexus',
       title: 'Galaxy Nexus',
       url: 'http://www.android.com/devices/detail/galaxy-nexus',
diff --git a/docs/html/gms_navtree_data.js b/docs/html/gms_navtree_data.js
index 86f31d9..51c01f64 100644
--- a/docs/html/gms_navtree_data.js
+++ b/docs/html/gms_navtree_data.js
@@ -15,10 +15,10 @@
 , null ], [ "Classes", null, [ [ "DataBuffer", "reference/com/google/android/gms/common/data/DataBuffer.html", null, null ], [ "DataBufferUtils", "reference/com/google/android/gms/common/data/DataBufferUtils.html", null, null ] ]
 , null ] ]
 , null ], [ "com.google.android.gms.common.images", "reference/com/google/android/gms/common/images/package-summary.html", [ [ "Interfaces", null, [ [ "ImageManager.OnImageLoadedListener", "reference/com/google/android/gms/common/images/ImageManager.OnImageLoadedListener.html", null, null ] ]
-, null ], [ "Classes", null, [ [ "ImageManager", "reference/com/google/android/gms/common/images/ImageManager.html", null, null ], [ "ImageManager.ImageReceiver", "reference/com/google/android/gms/common/images/ImageManager.ImageReceiver.html", null, null ] ]
+, null ], [ "Classes", null, [ [ "ImageManager", "reference/com/google/android/gms/common/images/ImageManager.html", null, null ] ]
 , null ] ]
 , null ], [ "com.google.android.gms.games", "reference/com/google/android/gms/games/package-summary.html", [ [ "Interfaces", null, [ [ "Game", "reference/com/google/android/gms/games/Game.html", null, null ], [ "OnGamesLoadedListener", "reference/com/google/android/gms/games/OnGamesLoadedListener.html", null, null ], [ "OnPlayersLoadedListener", "reference/com/google/android/gms/games/OnPlayersLoadedListener.html", null, null ], [ "OnSignOutCompleteListener", "reference/com/google/android/gms/games/OnSignOutCompleteListener.html", null, null ], [ "Player", "reference/com/google/android/gms/games/Player.html", null, null ], [ "RealTimeSocket", "reference/com/google/android/gms/games/RealTimeSocket.html", null, null ] ]
-, null ], [ "Classes", null, [ [ "GameBuffer", "reference/com/google/android/gms/games/GameBuffer.html", null, null ], [ "GameEntity", "reference/com/google/android/gms/games/GameEntity.html", null, null ], [ "GamesActivityResultCodes", "reference/com/google/android/gms/games/GamesActivityResultCodes.html", null, null ], [ "GamesClient", "reference/com/google/android/gms/games/GamesClient.html", null, null ], [ "GamesClient.Builder", "reference/com/google/android/gms/games/GamesClient.Builder.html", null, null ], [ "GamesClientSettings", "reference/com/google/android/gms/games/GamesClientSettings.html", null, null ], [ "PageDirection", "reference/com/google/android/gms/games/PageDirection.html", null, null ], [ "PlayerBuffer", "reference/com/google/android/gms/games/PlayerBuffer.html", null, null ], [ "PlayerEntity", "reference/com/google/android/gms/games/PlayerEntity.html", null, null ] ]
+, null ], [ "Classes", null, [ [ "GameBuffer", "reference/com/google/android/gms/games/GameBuffer.html", null, null ], [ "GameEntity", "reference/com/google/android/gms/games/GameEntity.html", null, null ], [ "GamesActivityResultCodes", "reference/com/google/android/gms/games/GamesActivityResultCodes.html", null, null ], [ "GamesClient", "reference/com/google/android/gms/games/GamesClient.html", null, null ], [ "GamesClient.Builder", "reference/com/google/android/gms/games/GamesClient.Builder.html", null, null ], [ "PageDirection", "reference/com/google/android/gms/games/PageDirection.html", null, null ], [ "PlayerBuffer", "reference/com/google/android/gms/games/PlayerBuffer.html", null, null ], [ "PlayerEntity", "reference/com/google/android/gms/games/PlayerEntity.html", null, null ] ]
 , null ] ]
 , null ], [ "com.google.android.gms.games.achievement", "reference/com/google/android/gms/games/achievement/package-summary.html", [ [ "Interfaces", null, [ [ "Achievement", "reference/com/google/android/gms/games/achievement/Achievement.html", null, null ], [ "OnAchievementsLoadedListener", "reference/com/google/android/gms/games/achievement/OnAchievementsLoadedListener.html", null, null ], [ "OnAchievementUpdatedListener", "reference/com/google/android/gms/games/achievement/OnAchievementUpdatedListener.html", null, null ] ]
 , null ], [ "Classes", null, [ [ "AchievementBuffer", "reference/com/google/android/gms/games/achievement/AchievementBuffer.html", null, null ] ]
diff --git a/docs/html/google/backup/signup.jd b/docs/html/google/backup/signup.jd
index 550d590..f9ad600 100644
--- a/docs/html/google/backup/signup.jd
+++ b/docs/html/google/backup/signup.jd
@@ -226,7 +226,7 @@
     } else if ($("input#agree").is(':checked')
         && packagename.length
         && packagename != DEFAULT_TEXT) {
-      window.location = "https://play.google.com/apps/publish/v2/GetBackupApiKey?p=" +
+      window.location = "https://play.google.com/apps/publish/GetBackupApiKey?p=" +
                       encodeURIComponent(packagename);
     } else {
       $("label#agreeLabel,label#pnameLabel").parent().stop().animate({color: "#258AAF"}, 200,
diff --git a/docs/html/google/index.jd b/docs/html/google/index.jd
index 4020cf4..095388e 100644
--- a/docs/html/google/index.jd
+++ b/docs/html/google/index.jd
@@ -1,6 +1,14 @@
 page.title=Google Services
 header.hide=1
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <style>
 div.landing-cell,
diff --git a/docs/html/google/play/billing/index.jd b/docs/html/google/play/billing/index.jd
index 481a79c..0818514 100644
--- a/docs/html/google/play/billing/index.jd
+++ b/docs/html/google/play/billing/index.jd
@@ -1,5 +1,13 @@
 page.title=Google Play In-app Billing
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <p>In-app Billing is a Google Play service that lets you sell digital content from inside
 your applications. You can use the service to sell a wide range of content, including downloadable
diff --git a/docs/html/guide/components/index.jd b/docs/html/guide/components/index.jd
index 87bae53..6ede873 100644
--- a/docs/html/guide/components/index.jd
+++ b/docs/html/guide/components/index.jd
@@ -4,6 +4,14 @@
 page.landing.image=images/develop/app_components.png
 
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <div class="landing-docs">
 
diff --git a/docs/html/guide/topics/manifest/uses-sdk-element.jd b/docs/html/guide/topics/manifest/uses-sdk-element.jd
index d5b5bdf..15092a0 100644
--- a/docs/html/guide/topics/manifest/uses-sdk-element.jd
+++ b/docs/html/guide/topics/manifest/uses-sdk-element.jd
@@ -227,6 +227,12 @@
 <table>
   <tr><th>Platform Version</th><th>API Level</th><th>VERSION_CODE</th><th>Notes</th></tr>
 
+    <tr><td><a href="{@docRoot}about/versions/android-4.3.html">Android 4.3</a></td>
+    <td><a href="{@docRoot}sdk/api_diff/18/changes.html" title="Diff Report">18</a></td>
+    <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}</td>
+    <td><a href="{@docRoot}about/versions/jelly-bean.html">Platform
+Highlights</a></td></tr>
+
     <tr><td><a href="{@docRoot}about/versions/android-4.2.html">Android 4.2, 4.2.2</a></td>
     <td><a href="{@docRoot}sdk/api_diff/17/changes.html" title="Diff Report">17</a></td>
     <td>{@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}</td>
diff --git a/docs/html/images/tools/studio_error_eventlog.png b/docs/html/images/tools/studio_error_eventlog.png
new file mode 100644
index 0000000..909b285
--- /dev/null
+++ b/docs/html/images/tools/studio_error_eventlog.png
Binary files differ
diff --git a/docs/html/images/tools/studio_error_gradle5.png b/docs/html/images/tools/studio_error_gradle5.png
new file mode 100644
index 0000000..13de607
--- /dev/null
+++ b/docs/html/images/tools/studio_error_gradle5.png
Binary files differ
diff --git a/docs/html/images/tools/studio_error_supportlib.png b/docs/html/images/tools/studio_error_supportlib.png
new file mode 100644
index 0000000..603b54c
--- /dev/null
+++ b/docs/html/images/tools/studio_error_supportlib.png
Binary files differ
diff --git a/docs/html/images/video-Colopl.png b/docs/html/images/video-Colopl.png
new file mode 100644
index 0000000..0ee88c6
--- /dev/null
+++ b/docs/html/images/video-Colopl.png
Binary files differ
diff --git a/docs/html/index.jd b/docs/html/index.jd
index d82deec..73fc302 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -3,6 +3,14 @@
 carousel=true
 page.metaDescription=The official site for Android developers. Provides the Android SDK and documentation for app developers and designers.
 @jd:body
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 
 <div class="wrap">
@@ -49,15 +57,15 @@
                 <li class="item carousel-home">
                     <div class="content-left col-11" style="padding-top:65px;">
                       <script src="//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js"></script>
-                      <div style="box-shadow: 3px 10px 18px 1px #999;width:600px;height:338px">
+                      <div style="box-shadow: 3px 10px 18px 1px #999;width:600px;height:336px">
                         <div id="ytapiplayer">
-                          You need Flash player 8+ and JavaScript enabled to view this video.
+                          <a href="http://www.youtube.com/watch?v=CbpoZeQCNe4"><img width=600 src="{@docRoot}images/video-Colopl.png"></a><!--You need Flash player 8+ and JavaScript enabled to view this video. -->
                         </div>
                         <script type="text/javascript">
                             var params = { allowScriptAccess: "always" };
                             var atts = { id: "ytapiplayer" };
-                            swfobject.embedSWF("//www.youtube.com/v/O8i4HUw7JYA?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1",
-                              "ytapiplayer", "600", "338", "8", null, null, params, atts);
+                            swfobject.embedSWF("//www.youtube.com/v/CbpoZeQCNe4?enablejsapi=1&playerapiid=ytplayer&version=3&HD=1;rel=0;showinfo=0;modestbranding;origin=developer.android.com;autohide=1",
+                              "ytapiplayer", "600", "336", "8", null, null, params, atts);
 
                             // Callback used to pause/resume carousel based on video state
                             function onytplayerStateChange(newState) {              
@@ -89,6 +97,37 @@
                     <p>Bangalore-based developers redBus.in talk about how Android is helping them deliver a superior booking and travel experience to millions of daily bus riders in India.</p>
                     </div>
                 </li>
+
+                <li class="item carousel-home">
+                    <div class="content-left col-11" style="padding-top:10px;">
+                        <a href="{@docRoot}channels/io2013.html">
+                          <img src="{@docRoot}images/home/io-videos-2013.png" style="margin:60px 0 0;
+                          box-shadow: 3px 10px 18px 1px #999;">
+                        </a>
+                    </div>
+                    <div class="content-right col-4">
+                    <h1>Watch the Android talks from Google I/O</h1>
+                    <p>If you weren't able to attend Google I/O in person or couldn't make it
+                    to all the talks, you can catch up on the action
+                    with all the recordings, brought to you by
+                    <a href="http://developers.google.com/live">Google Developers Live</a>.</p>
+                    <p><a href="{@docRoot}channels/io2013.html" class="button"
+                    >See the Android talks</a></p>
+                    </div>
+                </li>
+
+
+                <li class="item carousel-home">
+                    <div class="content-left col-9">
+                        <a href="{@docRoot}about/versions/jelly-bean.html"><img src="{@docRoot}images/home/android-jellybean.png" ></a>
+                    </div>
+                    <div class="content-right col-6">
+                    <h1>Android 4.2 Jelly Bean!</h1>
+                    <p>The latest version of Jelly Bean is here, with performance optimizations, a refreshed UI, and great new features for developers. </p>
+                    <p>Android 4.2 includes APIs for developing lock sceen widgets and Daydream screensavers, using external displays, creating RTL layouts, building flexible UI with nested Fragments, and much more.</p>
+                    <p><a href="{@docRoot}about/versions/jelly-bean.html" class="button">Learn More</a></p>
+                    </div>
+                </li>
                 <li class="item carousel-home">
                     <div class="content-left col-10">
                         <img src="{@docRoot}images/home/design.png" style="margin-top:30px">
diff --git a/docs/html/reference/com/google/android/gms/R.string.html b/docs/html/reference/com/google/android/gms/R.string.html
index f4fbd3a..9a353cd 100644
--- a/docs/html/reference/com/google/android/gms/R.string.html
+++ b/docs/html/reference/com/google/android/gms/R.string.html
@@ -778,9 +778,33 @@
           static
           
           int</nobr></td>
-          <td class="jd-linkcol"><a href="/reference/com/google/android/gms/R.string.html#auth_client_availability_notification_title">auth_client_availability_notification_title</a></td>
-          <td class="jd-descrcol" width="100%">Title for notification shown when GooglePlayServices is unavailable [CHAR LIMIT=70] 
-</td>
+          <td class="jd-linkcol"><a href="/reference/com/google/android/gms/R.string.html#auth_client_needs_enabling_title">auth_client_needs_enabling_title</a></td>
+          <td class="jd-descrcol" width="100%">Title for notification shown when GooglePlayServices needs to be
+        enabled for a application to work.</td>
+      </tr>
+      
+    
+      <tr class=" api apilevel-" >
+          <td class="jd-typecol"><nobr>
+          public
+          static
+          
+          int</nobr></td>
+          <td class="jd-linkcol"><a href="/reference/com/google/android/gms/R.string.html#auth_client_needs_installation_title">auth_client_needs_installation_title</a></td>
+          <td class="jd-descrcol" width="100%">Title for notification shown when GooglePlayServices needs to be
+        installed for a application to work.</td>
+      </tr>
+      
+    
+      <tr class="alt-color api apilevel-" >
+          <td class="jd-typecol"><nobr>
+          public
+          static
+          
+          int</nobr></td>
+          <td class="jd-linkcol"><a href="/reference/com/google/android/gms/R.string.html#auth_client_needs_update_title">auth_client_needs_update_title</a></td>
+          <td class="jd-descrcol" width="100%">Title for notification shown when GooglePlayServices needs to be
+        udpated for a application to work.</td>
       </tr>
       
     
@@ -813,12 +837,24 @@
           static
           
           int</nobr></td>
+          <td class="jd-linkcol"><a href="/reference/com/google/android/gms/R.string.html#auth_client_using_bad_version_title">auth_client_using_bad_version_title</a></td>
+          <td class="jd-descrcol" width="100%">Title for notification shown when a bad version of GooglePlayServices
+        has been installed and needs correction for an application to work.</td>
+      </tr>
+      
+    
+      <tr class="alt-color api apilevel-" >
+          <td class="jd-typecol"><nobr>
+          public
+          static
+          
+          int</nobr></td>
           <td class="jd-linkcol"><a href="/reference/com/google/android/gms/R.string.html#common_google_play_services_enable_button">common_google_play_services_enable_button</a></td>
           <td class="jd-descrcol" width="100%">Button in confirmation dialog to enable Google Play services.</td>
       </tr>
       
     
-      <tr class="alt-color api apilevel-" >
+      <tr class=" api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -831,7 +867,7 @@
       </tr>
       
     
-      <tr class=" api apilevel-" >
+      <tr class="alt-color api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -844,7 +880,7 @@
       </tr>
       
     
-      <tr class="alt-color api apilevel-" >
+      <tr class=" api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -856,7 +892,7 @@
       </tr>
       
     
-      <tr class=" api apilevel-" >
+      <tr class="alt-color api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -869,7 +905,7 @@
       </tr>
       
     
-      <tr class="alt-color api apilevel-" >
+      <tr class=" api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -882,7 +918,7 @@
       </tr>
       
     
-      <tr class=" api apilevel-" >
+      <tr class="alt-color api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -895,7 +931,7 @@
       </tr>
       
     
-      <tr class="alt-color api apilevel-" >
+      <tr class=" api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -908,7 +944,7 @@
       </tr>
       
     
-      <tr class=" api apilevel-" >
+      <tr class="alt-color api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -920,7 +956,7 @@
       </tr>
       
     
-      <tr class="alt-color api apilevel-" >
+      <tr class=" api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -932,7 +968,7 @@
       </tr>
       
     
-      <tr class=" api apilevel-" >
+      <tr class="alt-color api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -944,7 +980,7 @@
       </tr>
       
     
-      <tr class="alt-color api apilevel-" >
+      <tr class=" api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -957,7 +993,7 @@
       </tr>
       
     
-      <tr class=" api apilevel-" >
+      <tr class="alt-color api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -970,7 +1006,7 @@
       </tr>
       
     
-      <tr class="alt-color api apilevel-" >
+      <tr class=" api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -982,7 +1018,7 @@
       </tr>
       
     
-      <tr class=" api apilevel-" >
+      <tr class="alt-color api apilevel-" >
           <td class="jd-typecol"><nobr>
           public
           static
@@ -994,44 +1030,6 @@
       </tr>
       
     
-      <tr class="alt-color api apilevel-" >
-          <td class="jd-typecol"><nobr>
-          public
-          static
-          
-          int</nobr></td>
-          <td class="jd-linkcol"><a href="/reference/com/google/android/gms/R.string.html#location_client_ulr_inactive_age_under_13">location_client_ulr_inactive_age_under_13</a></td>
-          <td class="jd-descrcol" width="100%">Location client code resources (prefix with location_client) 
-
-        Begin strings for location.reporting
-
-
-        Some InactiveReason names.</td>
-      </tr>
-      
-    
-      <tr class=" api apilevel-" >
-          <td class="jd-typecol"><nobr>
-          public
-          static
-          
-          int</nobr></td>
-          <td class="jd-linkcol"><a href="/reference/com/google/android/gms/R.string.html#location_client_ulr_inactive_age_unknown">location_client_ulr_inactive_age_unknown</a></td>
-          <td class="jd-descrcol" width="100%"></td>
-      </tr>
-      
-    
-      <tr class="alt-color api apilevel-" >
-          <td class="jd-typecol"><nobr>
-          public
-          static
-          
-          int</nobr></td>
-          <td class="jd-linkcol"><a href="/reference/com/google/android/gms/R.string.html#location_client_ulr_inactive_unknown_restriction">location_client_ulr_inactive_unknown_restriction</a></td>
-          <td class="jd-descrcol" width="100%"></td>
-      </tr>
-      
-    
 
 </table>
 
@@ -1314,7 +1312,7 @@
 
 
 
-<A NAME="auth_client_availability_notification_title"></A>
+<A NAME="auth_client_needs_enabling_title"></A>
 
 <div class="jd-details api apilevel-"> 
     <h4 class="jd-details-title">
@@ -1324,7 +1322,7 @@
          
         int
       </span>
-        auth_client_availability_notification_title
+        auth_client_needs_enabling_title
     </h4>
       <div class="api-level">
         
@@ -1334,7 +1332,68 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Title for notification shown when GooglePlayServices is unavailable [CHAR LIMIT=70] 
+  <div class="jd-tagdata jd-tagdescr"><p>Title for notification shown when GooglePlayServices needs to be
+        enabled for a application to work. [CHAR LIMIT=70] 
+</p></div>
+
+    
+    </div>
+</div>
+
+
+
+<A NAME="auth_client_needs_installation_title"></A>
+
+<div class="jd-details api apilevel-"> 
+    <h4 class="jd-details-title">
+      <span class="normal">
+        public 
+        static 
+         
+        int
+      </span>
+        auth_client_needs_installation_title
+    </h4>
+      <div class="api-level">
+        
+        
+  
+
+      </div>
+    <div class="jd-details-descr">
+      
+  <div class="jd-tagdata jd-tagdescr"><p>Title for notification shown when GooglePlayServices needs to be
+        installed for a application to work. [CHAR LIMIT=70] 
+</p></div>
+
+    
+    </div>
+</div>
+
+
+
+<A NAME="auth_client_needs_update_title"></A>
+
+<div class="jd-details api apilevel-"> 
+    <h4 class="jd-details-title">
+      <span class="normal">
+        public 
+        static 
+         
+        int
+      </span>
+        auth_client_needs_update_title
+    </h4>
+      <div class="api-level">
+        
+        
+  
+
+      </div>
+    <div class="jd-details-descr">
+      
+  <div class="jd-tagdata jd-tagdescr"><p>Title for notification shown when GooglePlayServices needs to be
+        udpated for a application to work. [CHAR LIMIT=70] 
 </p></div>
 
     
@@ -1401,6 +1460,37 @@
 
 
 
+<A NAME="auth_client_using_bad_version_title"></A>
+
+<div class="jd-details api apilevel-"> 
+    <h4 class="jd-details-title">
+      <span class="normal">
+        public 
+        static 
+         
+        int
+      </span>
+        auth_client_using_bad_version_title
+    </h4>
+      <div class="api-level">
+        
+        
+  
+
+      </div>
+    <div class="jd-details-descr">
+      
+  <div class="jd-tagdata jd-tagdescr"><p>Title for notification shown when a bad version of GooglePlayServices
+        has been installed and needs correction for an application to work.
+        [CHAR LIMIT=70] 
+</p></div>
+
+    
+    </div>
+</div>
+
+
+
 <A NAME="common_google_play_services_enable_button"></A>
 
 <div class="jd-details api apilevel-"> 
@@ -1846,97 +1936,6 @@
 
 
 
-<A NAME="location_client_ulr_inactive_age_under_13"></A>
-
-<div class="jd-details api apilevel-"> 
-    <h4 class="jd-details-title">
-      <span class="normal">
-        public 
-        static 
-         
-        int
-      </span>
-        location_client_ulr_inactive_age_under_13
-    </h4>
-      <div class="api-level">
-        
-        
-  
-
-      </div>
-    <div class="jd-details-descr">
-      
-  <div class="jd-tagdata jd-tagdescr"><p>Location client code resources (prefix with location_client) 
-
-        Begin strings for location.reporting
-
-
-        Some InactiveReason names.  See more in OneUp/package/location/res/values/strings.xml
-
-</p></div>
-
-    
-    </div>
-</div>
-
-
-
-<A NAME="location_client_ulr_inactive_age_unknown"></A>
-
-<div class="jd-details api apilevel-"> 
-    <h4 class="jd-details-title">
-      <span class="normal">
-        public 
-        static 
-         
-        int
-      </span>
-        location_client_ulr_inactive_age_unknown
-    </h4>
-      <div class="api-level">
-        
-        
-  
-
-      </div>
-    <div class="jd-details-descr">
-      
-  <div class="jd-tagdata jd-tagdescr"><p></p></div>
-
-    
-    </div>
-</div>
-
-
-
-<A NAME="location_client_ulr_inactive_unknown_restriction"></A>
-
-<div class="jd-details api apilevel-"> 
-    <h4 class="jd-details-title">
-      <span class="normal">
-        public 
-        static 
-         
-        int
-      </span>
-        location_client_ulr_inactive_unknown_restriction
-    </h4>
-      <div class="api-level">
-        
-        
-  
-
-      </div>
-    <div class="jd-details-descr">
-      
-  <div class="jd-tagdata jd-tagdescr"><p></p></div>
-
-    
-    </div>
-</div>
-
-
-
 
 <!-- Public ctors -->
 
diff --git a/docs/html/reference/com/google/android/gms/appstate/AppState.html b/docs/html/reference/com/google/android/gms/appstate/AppState.html
index 287f031..564f1eb 100644
--- a/docs/html/reference/com/google/android/gms/appstate/AppState.html
+++ b/docs/html/reference/com/google/android/gms/appstate/AppState.html
@@ -995,7 +995,7 @@
   <div class="jd-tagdata jd-tagdescr"><p></p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the conflict data. Only valid if <code><a href="/reference/com/google/android/gms/appstate/AppState.html#hasConflict()">hasConflict()</a></code> is <code>true</code>.
+      <ul class="nolist"><li>The conflict data. Only valid if <code><a href="/reference/com/google/android/gms/appstate/AppState.html#hasConflict()">hasConflict()</a></code> is <code>true</code>.
 </li></ul>
   </div>
 
@@ -1029,7 +1029,7 @@
   <div class="jd-tagdata jd-tagdescr"><p></p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the latest known version of conflicting data from the server.
+      <ul class="nolist"><li>The latest known version of conflicting data from the server.
 </li></ul>
   </div>
 
@@ -1063,7 +1063,7 @@
   <div class="jd-tagdata jd-tagdescr"><p></p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the key associated with this app state blob.
+      <ul class="nolist"><li>The key associated with this app state blob.
 </li></ul>
   </div>
 
@@ -1097,7 +1097,7 @@
   <div class="jd-tagdata jd-tagdescr"><p></p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the local data for this app state blob, or null if none present.
+      <ul class="nolist"><li>The local data for this app state blob, or null if none present.
 </li></ul>
   </div>
 
@@ -1131,7 +1131,7 @@
   <div class="jd-tagdata jd-tagdescr"><p></p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the local version of the app state data.
+      <ul class="nolist"><li>The local version of the app state data.
 </li></ul>
   </div>
 
@@ -1165,7 +1165,7 @@
   <div class="jd-tagdata jd-tagdescr"><p></p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>whether or not this app state has conflict data to resolve.
+      <ul class="nolist"><li>Whether or not this app state has conflict data to resolve.
 </li></ul>
   </div>
 
diff --git a/docs/html/reference/com/google/android/gms/appstate/AppStateBuffer.html b/docs/html/reference/com/google/android/gms/appstate/AppStateBuffer.html
index f02cb98..785f9e7 100644
--- a/docs/html/reference/com/google/android/gms/appstate/AppStateBuffer.html
+++ b/docs/html/reference/com/google/android/gms/appstate/AppStateBuffer.html
@@ -1278,13 +1278,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the item at the specified position. Note that the objects returned from subsequent
  invocations of this method for the same position may not be identical objects, but will be
  equal in value. In other words:
-
- <pre>
- <code>
- buffer.get(i) == buffer.get(i) may return false.
- buffer.get(i).equals(buffer.get(i)) will return true.
- </code>
- </pre></p></div>
+ <p>
+ <code>buffer.get(i) == buffer.get(i)</code> may return false.
+ <p>
+ <code>buffer.get(i).equals(buffer.get(i))</code> will return true.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>position</td>
+          <td>The position of the item to retrieve.</td>
+        </tr>
+      </table>
+  </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>the item at <code>position</code> in this buffer.
diff --git a/docs/html/reference/com/google/android/gms/appstate/AppStateClient.Builder.html b/docs/html/reference/com/google/android/gms/appstate/AppStateClient.Builder.html
index 445ca0b..a6e3ed9 100644
--- a/docs/html/reference/com/google/android/gms/appstate/AppStateClient.Builder.html
+++ b/docs/html/reference/com/google/android/gms/appstate/AppStateClient.Builder.html
@@ -1146,16 +1146,16 @@
       <table class="jd-tagtable">
         <tr>
           <th>context</td>
-          <td>the context to use for the connection.</td>
+          <td>The context to use for the connection.</td>
         </tr>
         <tr>
           <th>connectedListener</td>
-          <td>the listener where the results of the asynchronous
+          <td>The listener where the results of the asynchronous
             <code><a href="/reference/com/google/android/gms/appstate/AppStateClient.html#connect()">connect()</a></code> call are delivered.</td>
         </tr>
         <tr>
           <th>connectionFailedListener</td>
-          <td>the listener which will be notified if the connection
+          <td>The listener which will be notified if the connection
             attempt fails.
 </td>
         </tr>
@@ -1262,11 +1262,15 @@
         <tr>
           <th>accountName</td>
           <td>The account name on the device that should be used by this
-            <code><a href="/reference/com/google/android/gms/appstate/AppStateClient.html">AppStateClient</a></code>. Must be non-null.
-</td>
+            <code><a href="/reference/com/google/android/gms/appstate/AppStateClient.html">AppStateClient</a></code>. Must be non-null.</td>
         </tr>
       </table>
   </div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>This Builder.
+</li></ul>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/com/google/android/gms/appstate/AppStateClient.html b/docs/html/reference/com/google/android/gms/appstate/AppStateClient.html
index 706df18..cf381b3 100644
--- a/docs/html/reference/com/google/android/gms/appstate/AppStateClient.html
+++ b/docs/html/reference/com/google/android/gms/appstate/AppStateClient.html
@@ -817,7 +817,7 @@
     <tr class=" api apilevel-" >
         <td class="jd-typecol">int</td>
         <td class="jd-linkcol"><a href="/reference/com/google/android/gms/appstate/AppStateClient.html#STATUS_DEVELOPER_ERROR">STATUS_DEVELOPER_ERROR</a></td>
-        <td class="jd-descrcol" width="100%">The developer has configured something incorrectly with their application.</td>
+        <td class="jd-descrcol" width="100%">Your application is incorrectly configured.</td>
     </tr>
     
     
@@ -870,8 +870,8 @@
     <tr class="alt-color api apilevel-" >
         <td class="jd-typecol">int</td>
         <td class="jd-linkcol"><a href="/reference/com/google/android/gms/appstate/AppStateClient.html#STATUS_STATE_KEY_LIMIT_EXCEEDED">STATUS_STATE_KEY_LIMIT_EXCEEDED</a></td>
-        <td class="jd-descrcol" width="100%">The application already has data in the maximum number of keys and is attempting to create a
- new one.</td>
+        <td class="jd-descrcol" width="100%">The application already has data in the maximum number of keys (data slots) and is attempting
+ to create a new one.</td>
     </tr>
     
     
@@ -1816,8 +1816,8 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>The developer has configured something incorrectly with their application. This is a hard
- error, since retrying will not fix this.
+  <div class="jd-tagdata jd-tagdescr"><p>Your application is incorrectly configured. This is a hard error, since retrying will not fix
+ this.
 </p></div>
 
     
@@ -2095,9 +2095,9 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>The application already has data in the maximum number of keys and is attempting to create a
- new one. This is a hard error. Subsequent writes to this same key will only succeed after
- some number of keys have been deleted.
+  <div class="jd-tagdata jd-tagdescr"><p>The application already has data in the maximum number of keys (data slots) and is attempting
+ to create a new one. This is a hard error. Subsequent writes to this same key will only
+ succeed after some number of keys have been deleted.
 </p></div>
 
     
@@ -2327,7 +2327,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. This listener is
+          <td>The listener that is called when the load is complete. This listener is
             required to be non-null. The listener is called on the main thread.</td>
         </tr>
         <tr>
@@ -2478,7 +2478,7 @@
  client actions caused by the user with a call to this method.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is connected to the service.
+      <ul class="nolist"><li>true if the client is connected to the service.
 </li></ul>
   </div>
 
@@ -2512,7 +2512,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Checks if the client is attempting to connect to the service.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is attempting to connect to the service.
+      <ul class="nolist"><li>true if the client is attempting to connect to the service.
 </li></ul>
   </div>
 
@@ -2556,7 +2556,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              events.</li></ul>
   </div>
 
@@ -2600,7 +2600,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              failed events.</li></ul>
   </div>
 
@@ -2640,7 +2640,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. This listener is
+          <td>The listener that is called when the load is complete. This listener is
             required to be non-null. The listener is called on the main thread.
 </td>
         </tr>
@@ -2683,7 +2683,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. This listener is
+          <td>The listener that is called when the load is complete. This listener is
             required to be non-null. The listener is called on the main thread.</td>
         </tr>
         <tr>
@@ -2765,9 +2765,10 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection events from this <code>GooglePlayServicesClient</code>.
- If we are already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code> method
- will be called immediately.  Applications should balance calls to this method with calls to
- <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking resources.
+ If the service is already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code>
+ method will be called immediately.  Applications should balance calls to this method with
+ calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection events, this
  method will not add a duplicate entry for the same listener, but <strong>will</strong>
@@ -2816,11 +2817,12 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection failed events from this
- <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if we are not
- already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code>
- method will not be called immediately.  Applications should balance calls to this method with
- calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid
- leaking resources.
+ <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if the service
+ is not already connected, the listener's
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code> method will not be called immediately.
+ Applications should balance calls to this method with calls to
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection failed events, this
  method will not add a duplicate entry for the same listener.
@@ -2880,7 +2882,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the operation is complete. This listener is
+          <td>The listener that is called when the operation is complete. This listener is
             required to be non-null. The listener is called on the main thread.</td>
         </tr>
         <tr>
@@ -2890,11 +2892,11 @@
         </tr>
         <tr>
           <th>resolvedVersion</td>
-          <td>version code from previous <code>onStateConflict</code> call.</td>
+          <td>Version code from previous <code>onStateConflict</code> call.</td>
         </tr>
         <tr>
           <th>resolvedData</td>
-          <td>data to submit as the current data. <code>null</code> is a valid value here.
+          <td>Data to submit as the current data. <code>null</code> is a valid value here.
             May be a maximum of <code><a href="/reference/com/google/android/gms/appstate/AppStateClient.html#getMaxStateSize()">getMaxStateSize()</a></code> bytes.
 </td>
         </tr>
@@ -2937,7 +2939,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when sign-out is complete. This listener is
+          <td>The listener that is called when sign-out is complete. This listener is
             required to be non-null. The listener is called on the main thread.
 </td>
         </tr>
@@ -3106,7 +3108,7 @@
         </tr>
         <tr>
           <th>data</td>
-          <td>the data to store. May be a maximum of <code><a href="/reference/com/google/android/gms/appstate/AppStateClient.html#getMaxStateSize()">getMaxStateSize()</a></code> bytes.
+          <td>The data to store. May be a maximum of <code><a href="/reference/com/google/android/gms/appstate/AppStateClient.html#getMaxStateSize()">getMaxStateSize()</a></code> bytes.
 </td>
         </tr>
       </table>
@@ -3151,7 +3153,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the write operation is complete. This
+          <td>The listener that is called when the write operation is complete. This
             listener is required to be non-null. The listener is called on the main thread.</td>
         </tr>
         <tr>
@@ -3161,7 +3163,7 @@
         </tr>
         <tr>
           <th>data</td>
-          <td>the data to store. May be a maximum of <code><a href="/reference/com/google/android/gms/appstate/AppStateClient.html#getMaxStateSize()">getMaxStateSize()</a></code> bytes.
+          <td>The data to store. May be a maximum of <code><a href="/reference/com/google/android/gms/appstate/AppStateClient.html#getMaxStateSize()">getMaxStateSize()</a></code> bytes.
 </td>
         </tr>
       </table>
diff --git a/docs/html/reference/com/google/android/gms/common/AccountPicker.html b/docs/html/reference/com/google/android/gms/common/AccountPicker.html
index f83a2e7..93da937 100644
--- a/docs/html/reference/com/google/android/gms/common/AccountPicker.html
+++ b/docs/html/reference/com/google/android/gms/common/AccountPicker.html
@@ -1130,28 +1130,28 @@
         </tr>
         <tr>
           <th>alwaysPromptForAccount</td>
-          <td>if set the account chooser screen is always shown, otherwise
- it is only shown when there is more than one account from which to choose</td>
+          <td>if set, the account chooser screen is always shown, otherwise
+ it is only shown when there is more than one account from which to choose.</td>
         </tr>
         <tr>
           <th>descriptionOverrideText</td>
           <td>if non-null this string is used as the description in the
- accounts chooser screen rather than the default</td>
+ accounts chooser screen rather than the default.</td>
         </tr>
         <tr>
           <th>addAccountAuthTokenType</td>
           <td>this string is passed as the <code><a href="/reference/android/accounts/AccountManager.html#addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)">addAccount(String, String, String[], Bundle, Activity, AccountManagerCallback<Bundle>, Handler)</a></code>
- authTokenType parameter</td>
+ authTokenType parameter.</td>
         </tr>
         <tr>
           <th>addAccountRequiredFeatures</td>
           <td>this string array is passed as the
- <code><a href="/reference/android/accounts/AccountManager.html#addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)">addAccount(String, String, String[], Bundle, Activity, AccountManagerCallback<Bundle>, Handler)</a></code> requiredFeatures parameter</td>
+ <code><a href="/reference/android/accounts/AccountManager.html#addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)">addAccount(String, String, String[], Bundle, Activity, AccountManagerCallback<Bundle>, Handler)</a></code> requiredFeatures parameter.</td>
         </tr>
         <tr>
           <th>addAccountOptions</td>
           <td>This <code><a href="/reference/android/os/Bundle.html">Bundle</a></code> is passed as the
- <code><a href="/reference/android/accounts/AccountManager.html#addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)">addAccount(String, String, String[], Bundle, Activity, AccountManagerCallback<Bundle>, Handler)</a></code> options parameter</td>
+ <code><a href="/reference/android/accounts/AccountManager.html#addAccount(java.lang.String, java.lang.String, java.lang.String[], android.os.Bundle, android.app.Activity, android.accounts.AccountManagerCallback<android.os.Bundle>, android.os.Handler)">addAccount(String, String, String[], Bundle, Activity, AccountManagerCallback<Bundle>, Handler)</a></code> options parameter.</td>
         </tr>
       </table>
   </div>
diff --git a/docs/html/reference/com/google/android/gms/common/ConnectionResult.html b/docs/html/reference/com/google/android/gms/common/ConnectionResult.html
index eaed4e5..5ca070d 100644
--- a/docs/html/reference/com/google/android/gms/common/ConnectionResult.html
+++ b/docs/html/reference/com/google/android/gms/common/ConnectionResult.html
@@ -1919,7 +1919,7 @@
  will start any intents requiring user interaction.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If there is a resolution that can be started.
+      <ul class="nolist"><li>true if there is a resolution that can be started.
 </li></ul>
   </div>
 
@@ -1953,7 +1953,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Returns true if the connection was successful.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the connection was successful, false if there was an error.
+      <ul class="nolist"><li>true if the connection was successful, false if there was an error.
 </li></ul>
   </div>
 
diff --git a/docs/html/reference/com/google/android/gms/common/GooglePlayServicesClient.html b/docs/html/reference/com/google/android/gms/common/GooglePlayServicesClient.html
index 05c531e..e768c22 100644
--- a/docs/html/reference/com/google/android/gms/common/GooglePlayServicesClient.html
+++ b/docs/html/reference/com/google/android/gms/common/GooglePlayServicesClient.html
@@ -764,7 +764,7 @@
 
 
 <h2>Class Overview</h2>
-<p itemprop="articleBody">
+<p itemprop="articleBody">Base class for clients that connect with Google Play services.
 </p>
 
 
@@ -1186,7 +1186,7 @@
  client actions caused by the user with a call to this method.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is connected to the service.
+      <ul class="nolist"><li>true if the client is connected to the service.
 </li></ul>
   </div>
 
@@ -1220,7 +1220,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Checks if the client is attempting to connect to the service.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is attempting to connect to the service.
+      <ul class="nolist"><li>true if the client is attempting to connect to the service.
 </li></ul>
   </div>
 
@@ -1264,7 +1264,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              events.</li></ul>
   </div>
   <div class="jd-tagdata">
@@ -1313,7 +1313,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              failed events.</li></ul>
   </div>
   <div class="jd-tagdata">
@@ -1350,9 +1350,10 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection events from this <code>GooglePlayServicesClient</code>.
- If we are already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code> method
- will be called immediately.  Applications should balance calls to this method with calls to
- <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking resources.
+ If the service is already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code>
+ method will be called immediately.  Applications should balance calls to this method with
+ calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection events, this
  method will not add a duplicate entry for the same listener, but <strong>will</strong>
@@ -1401,11 +1402,12 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection failed events from this
- <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if we are not
- already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code>
- method will not be called immediately.  Applications should balance calls to this method with
- calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid
- leaking resources.
+ <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if the service
+ is not already connected, the listener's
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code> method will not be called immediately.
+ Applications should balance calls to this method with calls to
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection failed events, this
  method will not add a duplicate entry for the same listener.
diff --git a/docs/html/reference/com/google/android/gms/common/GooglePlayServicesUtil.html b/docs/html/reference/com/google/android/gms/common/GooglePlayServicesUtil.html
index bb1ee84..a9d9023 100644
--- a/docs/html/reference/com/google/android/gms/common/GooglePlayServicesUtil.html
+++ b/docs/html/reference/com/google/android/gms/common/GooglePlayServicesUtil.html
@@ -1296,8 +1296,8 @@
         <span class="jd-tagtitle">Constant Value: </span>
         <span>
             
-                3136000
-                (0x002fda00)
+                3159000
+                (0x003033d8)
             
         </span>
         </div>
diff --git a/docs/html/reference/com/google/android/gms/common/data/DataBuffer.html b/docs/html/reference/com/google/android/gms/common/data/DataBuffer.html
index f9e7220..e5f2ea93 100644
--- a/docs/html/reference/com/google/android/gms/common/data/DataBuffer.html
+++ b/docs/html/reference/com/google/android/gms/common/data/DataBuffer.html
@@ -1362,13 +1362,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the item at the specified position. Note that the objects returned from subsequent
  invocations of this method for the same position may not be identical objects, but will be
  equal in value. In other words:
-
- <pre>
- <code>
- buffer.get(i) == buffer.get(i) may return false.
- buffer.get(i).equals(buffer.get(i)) will return true.
- </code>
- </pre></p></div>
+ <p>
+ <code>buffer.get(i) == buffer.get(i)</code> may return false.
+ <p>
+ <code>buffer.get(i).equals(buffer.get(i))</code> will return true.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>position</td>
+          <td>The position of the item to retrieve.</td>
+        </tr>
+      </table>
+  </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>the item at <code>position</code> in this buffer.
diff --git a/docs/html/reference/com/google/android/gms/common/images/ImageManager.ImageReceiver.html b/docs/html/reference/com/google/android/gms/common/images/ImageManager.ImageReceiver.html
deleted file mode 100644
index 72edcc3..0000000
--- a/docs/html/reference/com/google/android/gms/common/images/ImageManager.ImageReceiver.html
+++ /dev/null
@@ -1,1552 +0,0 @@
-<!DOCTYPE html>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-<html>
-<head>
-
-
-
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
-<title>ImageManager.ImageReceiver | Android Developers</title>
-
-<!-- STYLESHEETS -->
-<link rel="stylesheet"
-href="//fonts.googleapis.com/css?family=Roboto:regular,medium,thin,italic,mediumitalic,bold" title="roboto">
-<link href="/assets/css/default.css" rel="stylesheet" type="text/css">
-
-
-
-<!-- JAVASCRIPT -->
-<script src="//www.google.com/jsapi" type="text/javascript"></script>
-<script src="/assets/js/android_3p-bundle.js" type="text/javascript"></script>
-<script type="text/javascript">
-  var toRoot = "/";
-  var devsite = false;
-</script>
-<script src="/assets/js/docs.js" type="text/javascript"></script>
-
-<script type="text/javascript">
-  var _gaq = _gaq || [];
-  _gaq.push(['_setAccount', 'UA-5831155-1']);
-  _gaq.push(['_trackPageview']);
-
-  (function() {
-    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
-    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
-    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
-  })();
-</script>
-</head>
-<body class="gc-documentation google
-  develop" itemscope itemtype="http://schema.org/Article">
-  <div id="doc-api-level" class="" style="display:none"></div>
-  <a name="top"></a>
-
-<a name="top"></a>
-
-    <!-- Header -->
-    <div id="header">
-        <div class="wrap" id="header-wrap">
-          <div class="col-3 logo">
-          <a href="/index.html">
-            <img src="/assets/images/dac_logo.png" width="123" height="25" alt="Android Developers" />
-          </a>
-          <div class="btn-quicknav" id="btn-quicknav">
-          	<a href="#" class="arrow-inactive">Quicknav</a>
-			      <a href="#" class="arrow-active">Quicknav</a>
-          </div>
-          </div>
-            <ul class="nav-x col-9">
-                <li class="design">
-                  <a href="/design/index.html"
-                  zh-tw-lang="設計"
-                  zh-cn-lang="设计"
-                  ru-lang="Проектирование"
-                  ko-lang="디자인"
-                  ja-lang="設計"
-                  es-lang="Diseñar"               
-                  >Design</a></li>
-                <li class="develop"><a href="/develop/index.html"
-                  zh-tw-lang="開發"
-                  zh-cn-lang="开发"
-                  ru-lang="Разработка"
-                  ko-lang="개발"
-                  ja-lang="開発"
-                  es-lang="Desarrollar"               
-                  >Develop</a></li>
-                <li class="distribute last"><a href="/distribute/index.html"
-                  zh-tw-lang="發佈"
-                  zh-cn-lang="分发"
-                  ru-lang="Распространение"
-                  ko-lang="배포"
-                  ja-lang="配布"
-                  es-lang="Distribuir"               
-                  >Distribute</a></li>
-            </ul>
-            
-            <!-- New Search -->
-            <div class="menu-container">
-            <div class="moremenu">
-    <div id="more-btn"></div>
-  </div>
-  <div class="morehover" id="moremenu">
-    <div class="top"></div>
-    <div class="mid">
-      <div class="header">Links</div>
-      <ul>
-        <li><a href="https://play.google.com/apps/publish/">Google Play Developer Console</a></li>
-        <li><a href="http://android-developers.blogspot.com/">Android Developers Blog</a></li>
-        <li><a href="/about/index.html">About Android</a></li>
-      </ul>
-      <div class="header">Android Sites</div>
-      <ul>
-        <li><a href="http://www.android.com">Android.com</a></li>
-        <li class="active"><a>Android Developers</a></li>
-        <li><a href="http://source.android.com">Android Open Source Project</a></li>
-      </ul>
-      
-      
-      
-        <div class="header">Language</div>
-          <div id="language" class="locales">
-            <select name="language" onChange="changeLangPref(this.value, true)">
-                <option value="en">English</option>
-                <option value="es">Español</option>
-                <option value="ja">日本語</option>
-                <option value="ko">한국어</option>
-                <option value="ru">Русский</option>
-                <option value="zh-cn">中文 (中国)</option>
-                <option value="zh-tw">中文 (台灣)</option>
-            </select>
-          </div>
-        <script type="text/javascript">
-          <!--
-          loadLangPref();
-            //-->
-        </script>
-      
-      
-
-
-      <br class="clearfix" />
-    </div>
-    <div class="bottom"></div>
-  </div>
-  <div class="search" id="search-container">
-    <div class="search-inner">
-      <div id="search-btn"></div>
-      <div class="left"></div>
-      <form onsubmit="return submit_search()">
-        <input id="search_autocomplete" type="text" value="" autocomplete="off" name="q"
-onfocus="search_focus_changed(this, true)" onblur="search_focus_changed(this, false)"
-onkeydown="return search_changed(event, true, '/')" 
-onkeyup="return search_changed(event, false, '/')" />
-      </form>
-      <div class="right"></div>
-        <a class="close hide">close</a>
-        <div class="left"></div>
-        <div class="right"></div>
-    </div>
-  </div>
-
-  <div class="search_filtered_wrapper reference">
-    <div class="suggest-card reference no-display">
-      <ul class="search_filtered">
-      </ul>
-    </div>
-  </div>
-
-  <div class="search_filtered_wrapper docs">
-    <div class="suggest-card dummy no-display">&nbsp;</div>
-    <div class="suggest-card develop no-display">
-      <ul class="search_filtered">
-      </ul>
-      <div class="child-card guides no-display">
-      </div>
-      <div class="child-card training no-display">
-      </div>
-    </div>
-    <div class="suggest-card design no-display">
-      <ul class="search_filtered">
-      </ul>
-    </div>
-    <div class="suggest-card distribute no-display">
-      <ul class="search_filtered">
-      </ul>
-    </div>
-  </div>
-
-  </div>
-  <!-- /New Search>
-          
-          
-          <!-- Expanded quicknav -->
-           <div id="quicknav" class="col-9">
-                <ul>
-                    <li class="design">
-                      <ul>
-                        <li><a href="/design/index.html">Get Started</a></li>
-                        <li><a href="/design/style/index.html">Style</a></li>
-                        <li><a href="/design/patterns/index.html">Patterns</a></li>
-                        <li><a href="/design/building-blocks/index.html">Building Blocks</a></li>
-                        <li><a href="/design/downloads/index.html">Downloads</a></li>
-                        <li><a href="/design/videos/index.html">Videos</a></li>
-                      </ul>
-                    </li>
-                    <li class="develop">
-                      <ul>
-                        <li><a href="/training/index.html"
-                          zh-tw-lang="訓練課程"
-                          zh-cn-lang="培训"
-                          ru-lang="Курсы"
-                          ko-lang="교육"
-                          ja-lang="トレーニング"
-                          es-lang="Capacitación"               
-                          >Training</a></li>
-                        <li><a href="/guide/components/index.html"
-                          zh-tw-lang="API 指南"
-                          zh-cn-lang="API 指南"
-                          ru-lang="Руководства по API"
-                          ko-lang="API 가이드"
-                          ja-lang="API ガイド"
-                          es-lang="Guías de la API"               
-                          >API Guides</a></li>
-                        <li><a href="/reference/packages.html"
-                          zh-tw-lang="參考資源"
-                          zh-cn-lang="参考"
-                          ru-lang="Справочник"
-                          ko-lang="참조문서"
-                          ja-lang="リファレンス"
-                          es-lang="Referencia"               
-                          >Reference</a></li>
-                        <li><a href="/tools/index.html"
-                          zh-tw-lang="相關工具"
-                          zh-cn-lang="工具"
-                          ru-lang="Инструменты"
-                          ko-lang="도구"
-                          ja-lang="ツール"
-                          es-lang="Herramientas"               
-                          >Tools</a>
-                          <ul><li><a href="/sdk/index.html">Get the SDK</a></li></ul>
-                        </li>
-                        <li><a href="/google/index.html">Google Services</a>
-                        </li>
-                      </ul>
-                    </li>
-                    <li class="distribute last">
-                      <ul>
-                        <li><a href="/distribute/index.html">Google Play</a></li>
-                        <li><a href="/distribute/googleplay/publish/index.html">Publishing</a></li>
-                        <li><a href="/distribute/googleplay/promote/index.html">Promoting</a></li>
-                        <li><a href="/distribute/googleplay/quality/index.html">App Quality</a></li>
-                        <li><a href="/distribute/googleplay/spotlight/index.html">Spotlight</a></li>
-                        <li><a href="/distribute/open.html">Open Distribution</a></li>
-                      </ul>
-                    </li>
-                </ul>
-          </div>
-          <!-- /Expanded quicknav -->
-        </div>
-    </div>
-    <!-- /Header -->
-    
-    
-  <div id="searchResults" class="wrap" style="display:none;">
-          <h2 id="searchTitle">Results</h2>
-          <div id="leftSearchControl" class="search-control">Loading...</div>
-  </div>
-    
-    
-  
-    <!-- Secondary x-nav -->
-    <div id="nav-x">
-        <div class="wrap">
-            <ul class="nav-x col-9 develop" style="width:100%">
-                <li class="training"><a href="/training/index.html"
-                  zh-tw-lang="訓練課程"
-                  zh-cn-lang="培训"
-                  ru-lang="Курсы"
-                  ko-lang="교육"
-                  ja-lang="トレーニング"
-                  es-lang="Capacitación"               
-                  >Training</a></li>
-                <li class="guide"><a href="/guide/components/index.html"
-                  zh-tw-lang="API 指南"
-                  zh-cn-lang="API 指南"
-                  ru-lang="Руководства по API"
-                  ko-lang="API 가이드"
-                  ja-lang="API ガイド"
-                  es-lang="Guías de la API"               
-                  >API Guides</a></li>
-                <li class="reference"><a href="/reference/packages.html"
-                  zh-tw-lang="參考資源"
-                  zh-cn-lang="参考"
-                  ru-lang="Справочник"
-                  ko-lang="참조문서"
-                  ja-lang="リファレンス"
-                  es-lang="Referencia"               
-                  >Reference</a></li>
-                <li class="tools"><a href="/tools/index.html"
-                  zh-tw-lang="相關工具"
-                  zh-cn-lang="工具"
-                  ru-lang="Инструменты"
-                  ko-lang="도구"
-                  ja-lang="ツール"
-                  es-lang="Herramientas"
-                  >Tools</a></li>
-                <li class="google"><a href="/google/index.html"
-                  >Google Services</a>
-                </li>
-            </ul>
-        </div>
-        
-    </div>
-    <!-- /Sendondary x-nav -->
-  
-
-
-
-
-  
-
-
-  
-  <div class="wrap clearfix" id="body-content">
-    <div class="col-4" id="side-nav" itemscope itemtype="http://schema.org/SiteNavigationElement">
-      <div id="devdoc-nav" class="scroll-pane">
-<a class="totop" href="#top" data-g-event="left-nav-top">to top</a>
-
-
-
-<ul id="nav">
-
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/index.html">
-          <span class="en">Overview</span>
-      </a></div>
-  </li>
-
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/play-services/games.html">
-          <span class="en">Games</span>
-      </a></div>
-  </li>
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/play-services/location.html">
-          <span class="en">Location</span>
-      </a></div>
-  </li>
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/play-services/plus.html">
-          <span class="en">Google+</span>
-                </a></div>
-  </li>
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/play-services/maps.html">
-          <span class="en">Google Maps</span>
-      </a></div>
-  </li>
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/play-services/auth.html">
-          <span class="en">Authorization</span>
-      </a></div>
-  </li>
-
-
-
-  <li class="nav-section">
-    <div class="nav-section-header"><a href="/google/play-services/index.html">
-      <span class="en">Google Play Services</span></a>
-    </div>
-    <ul>
-      <li><a href="/google/play-services/setup.html">
-          <span class="en">Setup</span></a>
-      </li>
-      <li id="gms-tree-list" class="nav-section">
-        <div class="nav-section-header">
-          <a href="/reference/gms-packages.html">
-            <span class="en">Reference</span>
-          </a>
-        <div>
-      </li>
-    </ul>
-  </li>
-
-
-  <li class="nav-section">
-    <div class="nav-section-header"><a href="/google/play/billing/index.html">
-      <span class="en">Google Play In-app Billing</span></a>
-    </div>
-    <ul>
-      <li><a href="/google/play/billing/billing_overview.html">
-              <span class="en">Overview</span></a>
-      </li>
-      <li class="nav-section"><div class="nav-section-header"><a href="/google/play/billing/api.html">
-              <span class="en">Version 3 API</span></a></div>
-              <ul>
-              <li><a href="/google/play/billing/billing_integrate.html">
-              <span class="en">Implementing the API</span></a></li>
-              <li><a href="/google/play/billing/billing_reference.html">
-              <span class="en">Reference</span></a></li>
-              </ul>
-      </li>
-      <li class="nav-section"><div class="nav-section-header"><a href="/google/play/billing/v2/api.html">
-              <span class="en">Version 2 API</span></a></div>
-              <ul>
-              <li><a href="/google/play/billing/v2/billing_integrate.html">
-              <span class="en">Implementing the API</span></a></li>
-              <li><a href="/google/play/billing/v2/billing_subscriptions.html">
-              <span class="en">Subscriptions</span></a></li>
-              <li><a href="/google/play/billing/v2/billing_reference.html">
-              <span class="en">Reference</span></a></li>
-              </ul>
-      </li>
-      <li><a href="/google/play/billing/billing_subscriptions.html">
-              <span class="en">Subscriptions</span></a>
-      </li>
-      <li><a href="/google/play/billing/billing_best_practices.html">
-              <span class="en">Security and Design</span></a>
-      </li>
-      <li><a href="/google/play/billing/billing_testing.html">
-              <span class="en">Testing In-app Billing</span></a>
-      </li>
-      <li><a href="/google/play/billing/billing_admin.html">
-              <span class="en">Administering In-app Billing</span></a>
-      </li>
-      <li><a href="/google/play/billing/gp-purchase-status-api.html">
-              <span class="en">Purchase Status API</span></a>
-      </li>
-      <li><a href="/google/play/billing/versions.html">
-              <span class="en">Version Notes</span></a>
-      </li>
-    </ul>
-  </li>
-
-
-
-  <li class="nav-section">
-      <div class="nav-section-header"><a href="/google/gcm/index.html">
-        <span class="en">Google Cloud Messaging</span></a>
-      </div>
-      <ul>
-        <li><a href="/google/gcm/gs.html">
-            <span class="en">Getting Started</span></a>
-        </li>
-        <li><a href="/google/gcm/gcm.html">
-            <span class="en">Architectural Overview</span></a>
-        </li>
-         <li><a href="/google/gcm/ccs.html">
-              <span class="en">Cloud Connection Server</span></a>
-        </li>
-        <li><a href="/google/gcm/notifications.html">
-              <span class="en">User Notifications</span></a>
-        </li>
-        <li><a href="/google/gcm/client.html">
-            <span class="en">GCM Client</span></a>
-        </li>
-        <li><a href="/google/gcm/server.html">
-            <span class="en">GCM Server</span></a>
-        </li>
-        <li><a href="/google/gcm/adv.html">
-            <span class="en">Advanced Topics</span></a>
-        </li>
-        <li><a href="/google/gcm/c2dm.html">
-            <span class="en">Migration</span></a>
-        </li>
-        <li id="gcm-tree-list" class="nav-section">
-          <div class="nav-section-header">
-            <a href="/reference/gcm-packages.html">
-              <span class="en">Reference</span>
-            </a>
-          <div>
-        </li>
-      </ul>
-  </li>
-
-  <li class="nav-section">
-    <div class="nav-section-header"><a href="/google/play/dist.html">
-      <span class="en">Google Play Distribution</span></a>
-    </div>
-    <ul>
-      <li><a href="/google/play/filters.html">
-          <span class="en">Filters on Google Play</span></a>
-      </li>
-
-      <li><a href="/google/play/publishing/multiple-apks.html">
-          <span class="en">Multiple APK Support</span></a>
-      </li>
-      <li><a href="/google/play/expansion-files.html">
-          <span class="en">APK Expansion Files</span></a>
-      </li>
-      <li class="nav-section">
-        <div class="nav-section-header"><a href="/google/play/licensing/index.html">
-          <span class="en">Application Licensing</span></a>
-        </div>
-        <ul>
-          <li><a href="/google/play/licensing/overview.html">
-              <span class="en">Licensing Overview</span></a>
-          </li>
-          <li><a href="/google/play/licensing/setting-up.html">
-              <span class="en">Setting Up for Licensing</span></a>
-          </li>
-          <li><a href="/google/play/licensing/adding-licensing.html">
-              <span class="en">Adding Licensing to Your App</span></a>
-          </li>
-          <li><a href="/google/play/licensing/licensing-reference.html">
-              <span class="en">Licensing Reference</span></a>
-          </li>
-        </ul>
-      </li>
-
-  <li class="nav-section">
-    <div class="nav-section-header"><a href="/google/backup/index.html">
-      Android Backup Service</a>
-    </div>
-    <ul>
-      <li><a href="/google/backup/signup.html">
-          Register</a>
-      </li>
-    </ul>
-  </li>
-
-  </ul>
-
-</li>
-
-
-
-</ul>
-
-<script type="text/javascript">
-<!--
-    buildToggleLists();
-    changeNavLang(getLangPref());
-//-->
-</script>
-
-
-        
-
-      </div>
-      <script type="text/javascript">
-       showGoogleRefTree();
-    
-      </script>
-    </div> <!-- end side-nav -->
-    <script>
-      $(document).ready(function() {
-        scrollIntoView("devdoc-nav");
-        });
-    </script>
-
-
-     
-
-
-
-<div class="col-12"  id="doc-col">
-
-<div id="api-info-block">
-
-
-
-  
-   
-  
-  
-  
-   
-  
-  
-
-  
-   
-  
-  
-  
-  
-
-  
-   
-  
-  
-   
-  
-  
-  
-
-
-<div class="sum-details-links">
-
-Summary:
-
-
-
-
-
-
-  <a href="#inhconstants">Inherited Constants</a>
-  
-
-
-
-  &#124; <a href="#inhfields">Inherited Fields</a>
-  
-
-
-
-
-  &#124; <a href="#pubmethods">Methods</a>
-  
-
-
-
-  &#124; <a href="#inhmethods">Inherited Methods</a>
-
-&#124; <a href="#" onclick="return toggleAllClassInherited()" id="toggleAllClassInherited">[Expand All]</a>
-
-</div><!-- end sum-details-links -->
-<div class="api-level">
-  
-  
-  
-
-</div>
-</div><!-- end api-info-block -->
-
-
-<!-- ======== START OF CLASS DATA ======== -->
-
-<div id="jd-header">
-    protected
-     
-    final 
-    
-    class
-<h1 itemprop="name">ImageManager.ImageReceiver</h1>
-
-
-
-  
-  
-  
-
-  
-    extends ResultReceiver<br/>
-  
-  
-  
-
-  
-  
-  
-
-
-</div><!-- end header -->
-
-<div id="naMessage"></div>
-
-<div id="jd-content" class="api apilevel-">
-<table class="jd-inheritance-table">
-
-
-    <tr>
-         	
-        <td colspan="3" class="jd-inheritance-class-cell">java.lang.Object</td>
-    </tr>
-    
-
-    <tr>
-        
-            <td class="jd-inheritance-space">&nbsp;&nbsp;&nbsp;&#x21b3;</td>
-         	
-        <td colspan="2" class="jd-inheritance-class-cell">android.os.ResultReceiver</td>
-    </tr>
-    
-
-    <tr>
-        
-            <td class="jd-inheritance-space">&nbsp;</td>
-        
-            <td class="jd-inheritance-space">&nbsp;&nbsp;&nbsp;&#x21b3;</td>
-         	
-        <td colspan="1" class="jd-inheritance-class-cell">com.google.android.gms.common.images.ImageManager.ImageReceiver</td>
-    </tr>
-    
-
-</table>
-
-
-
-
-
-
-
-<div class="jd-descr">
-
-
-
-
-
-
-</div><!-- jd-descr -->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-<div class="jd-descr">
-
-
-<h2>Summary</h2>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-<!-- =========== ENUM CONSTANT SUMMARY =========== -->
-<table id="inhconstants" class="jd-sumtable"><tr><th>
-  <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
-  <div style="clear:left;">Inherited Constants</div></th></tr>
-
-
-
-
-
-
-<tr class="api apilevel-" >
-<td colspan="12">
-
-  <a href="#" onclick="return toggleInherited(this, null)" id="inherited-constants-android.os.Parcelable" class="jd-expando-trigger closed"
-          ><img id="inherited-constants-android.os.Parcelable-trigger"
-          src="/assets/images/triangle-closed.png"
-          class="jd-expando-trigger-img" /></a>From interface
-android.os.Parcelable
-<div id="inherited-constants-android.os.Parcelable">
-  <div id="inherited-constants-android.os.Parcelable-list"
-        class="jd-inheritedlinks">
-  </div>
-  <div id="inherited-constants-android.os.Parcelable-summary" style="display: none;">
-    <table class="jd-sumtable-expando">
-    
-
-    
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol">int</td>
-        <td class="jd-linkcol">CONTENTS_FILE_DESCRIPTOR</td>
-        <td class="jd-descrcol" width="100%"></td>
-    </tr>
-    
-    
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol">int</td>
-        <td class="jd-linkcol">PARCELABLE_WRITE_RETURN_VALUE</td>
-        <td class="jd-descrcol" width="100%"></td>
-    </tr>
-    
-    
-</table>
-  </div>
-</div>
-</td></tr>
-
-
-</table>
-
-
-
-
-
-
-
-<!-- =========== FIELD SUMMARY =========== -->
-<table id="inhfields" class="jd-sumtable"><tr><th>
-  <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
-  <div style="clear:left;">Inherited Fields</div></th></tr>
-
-
-<tr class="api apilevel-" >
-<td colspan="12">
-
-  <a href="#" onclick="return toggleInherited(this, null)" id="inherited-fields-android.os.ResultReceiver" class="jd-expando-trigger closed"
-          ><img id="inherited-fields-android.os.ResultReceiver-trigger"
-          src="/assets/images/triangle-closed.png"
-          class="jd-expando-trigger-img" /></a>From class
-android.os.ResultReceiver
-<div id="inherited-fields-android.os.ResultReceiver">
-  <div id="inherited-fields-android.os.ResultReceiver-list"
-        class="jd-inheritedlinks">
-  </div>
-  <div id="inherited-fields-android.os.ResultReceiver-summary" style="display: none;">
-    <table class="jd-sumtable-expando">
-    
-
-    
-      <tr class="alt-color api apilevel-" >
-          <td class="jd-typecol"><nobr>
-          public
-          static
-          final
-          Creator&lt;ResultReceiver&gt;</nobr></td>
-          <td class="jd-linkcol">CREATOR</td>
-          <td class="jd-descrcol" width="100%"></td>
-      </tr>
-      
-    
-</table>
-  </div>
-</div>
-</td></tr>
-
-
-
-
-
-
-</table>
-
-
-
-
-
-
-
-
-<!-- ========== METHOD SUMMARY =========== -->
-<table id="pubmethods" class="jd-sumtable"><tr><th colspan="12">Public Methods</th></tr>
-
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad"><a href="/reference/com/google/android/gms/common/images/ImageManager.ImageReceiver.html#addOnImageLoadedListenerHolder(com.google.android.gms.common.images.ImageManager.ListenerHolder)">addOnImageLoadedListenerHolder</a></span>(ImageManager.ListenerHolder imageViewLoadListener)</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            Uri</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad"><a href="/reference/com/google/android/gms/common/images/ImageManager.ImageReceiver.html#getUri()">getUri</a></span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad"><a href="/reference/com/google/android/gms/common/images/ImageManager.ImageReceiver.html#onReceiveResult(int, android.os.Bundle)">onReceiveResult</a></span>(int resultCode, Bundle resultData)</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad"><a href="/reference/com/google/android/gms/common/images/ImageManager.ImageReceiver.html#removeOnImageLoadedListenerHolder(com.google.android.gms.common.images.ImageManager.ListenerHolder)">removeOnImageLoadedListenerHolder</a></span>(ImageManager.ListenerHolder imageViewLoadListener)</nobr>
-        
-  </td></tr>
-
-
-
-</table>
-
-
-
-
-
-
-
-<!-- ========== METHOD SUMMARY =========== -->
-<table id="inhmethods" class="jd-sumtable"><tr><th>
-  <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
-  <div style="clear:left;">Inherited Methods</div></th></tr>
-
-
-<tr class="api apilevel-" >
-<td colspan="12">
-  <a href="#" onclick="return toggleInherited(this, null)" id="inherited-methods-android.os.ResultReceiver" class="jd-expando-trigger closed"
-          ><img id="inherited-methods-android.os.ResultReceiver-trigger"
-          src="/assets/images/triangle-closed.png"
-          class="jd-expando-trigger-img" /></a>
-From class
-
-  android.os.ResultReceiver
-
-<div id="inherited-methods-android.os.ResultReceiver">
-  <div id="inherited-methods-android.os.ResultReceiver-list"
-        class="jd-inheritedlinks">
-  </div>
-  <div id="inherited-methods-android.os.ResultReceiver-summary" style="display: none;">
-    <table class="jd-sumtable-expando">
-    
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            int</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">describeContents</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">onReceiveResult</span>(int arg0, Bundle arg1)</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">send</span>(int arg0, Bundle arg1)</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">writeToParcel</span>(Parcel arg0, int arg1)</nobr>
-        
-  </td></tr>
-
-
-</table>
-  </div>
-</div>
-</td></tr>
-
-
-
-<tr class="api apilevel-" >
-<td colspan="12">
-  <a href="#" onclick="return toggleInherited(this, null)" id="inherited-methods-java.lang.Object" class="jd-expando-trigger closed"
-          ><img id="inherited-methods-java.lang.Object-trigger"
-          src="/assets/images/triangle-closed.png"
-          class="jd-expando-trigger-img" /></a>
-From class
-
-  java.lang.Object
-
-<div id="inherited-methods-java.lang.Object">
-  <div id="inherited-methods-java.lang.Object-list"
-        class="jd-inheritedlinks">
-  </div>
-  <div id="inherited-methods-java.lang.Object-summary" style="display: none;">
-    <table class="jd-sumtable-expando">
-    
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            Object</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">clone</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            boolean</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">equals</span>(Object arg0)</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">finalize</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            Class&lt;?&gt;</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">getClass</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            int</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">hashCode</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">notify</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">notifyAll</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            String</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">toString</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">wait</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">wait</span>(long arg0, int arg1)</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">wait</span>(long arg0)</nobr>
-        
-  </td></tr>
-
-
-</table>
-  </div>
-</div>
-</td></tr>
-
-
-
-<tr class="api apilevel-" >
-<td colspan="12">
-  <a href="#" onclick="return toggleInherited(this, null)" id="inherited-methods-android.os.Parcelable" class="jd-expando-trigger closed"
-          ><img id="inherited-methods-android.os.Parcelable-trigger"
-          src="/assets/images/triangle-closed.png"
-          class="jd-expando-trigger-img" /></a>
-From interface
-
-  android.os.Parcelable
-
-<div id="inherited-methods-android.os.Parcelable">
-  <div id="inherited-methods-android.os.Parcelable-list"
-        class="jd-inheritedlinks">
-  </div>
-  <div id="inherited-methods-android.os.Parcelable-summary" style="display: none;">
-    <table class="jd-sumtable-expando">
-    
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            abstract
-            
-            
-            
-            
-            int</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">describeContents</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            abstract
-            
-            
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">writeToParcel</span>(Parcel arg0, int arg1)</nobr>
-        
-  </td></tr>
-
-
-</table>
-  </div>
-</div>
-</td></tr>
-
-
-</table>
-
-
-</div><!-- jd-descr (summary) -->
-
-<!-- Details -->
-
-
-
-
-
-
-
-
-<!-- XML Attributes -->
-
-
-<!-- Enum Values -->
-
-
-<!-- Constants -->
-
-
-<!-- Fields -->
-
-
-<!-- Public ctors -->
-
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-<!-- Protected ctors -->
-
-
-
-<!-- ========= METHOD DETAIL ======== -->
-<!-- Public methdos -->
-
-<h2>Public Methods</h2>
-
-
-
-<A NAME="addOnImageLoadedListenerHolder(com.google.android.gms.common.images.ImageManager.ListenerHolder)"></A>
-
-<div class="jd-details api apilevel-"> 
-    <h4 class="jd-details-title">
-      <span class="normal">
-        public 
-         
-         
-         
-         
-        void
-      </span>
-      <span class="sympad">addOnImageLoadedListenerHolder</span>
-      <span class="normal">(ImageManager.ListenerHolder imageViewLoadListener)</span>
-    </h4>
-      <div class="api-level">
-        <div></div>
-        
-  
-
-      </div>
-    <div class="jd-details-descr">
-      
-  <div class="jd-tagdata jd-tagdescr"><p></p></div>
-
-    </div>
-</div>
-
-
-<A NAME="getUri()"></A>
-
-<div class="jd-details api apilevel-"> 
-    <h4 class="jd-details-title">
-      <span class="normal">
-        public 
-         
-         
-         
-         
-        Uri
-      </span>
-      <span class="sympad">getUri</span>
-      <span class="normal">()</span>
-    </h4>
-      <div class="api-level">
-        <div></div>
-        
-  
-
-      </div>
-    <div class="jd-details-descr">
-      
-  <div class="jd-tagdata jd-tagdescr"><p></p></div>
-
-    </div>
-</div>
-
-
-<A NAME="onReceiveResult(int, android.os.Bundle)"></A>
-
-<div class="jd-details api apilevel-"> 
-    <h4 class="jd-details-title">
-      <span class="normal">
-        public 
-         
-         
-         
-         
-        void
-      </span>
-      <span class="sympad">onReceiveResult</span>
-      <span class="normal">(int resultCode, Bundle resultData)</span>
-    </h4>
-      <div class="api-level">
-        <div></div>
-        
-  
-
-      </div>
-    <div class="jd-details-descr">
-      
-  <div class="jd-tagdata jd-tagdescr"><p></p></div>
-
-    </div>
-</div>
-
-
-<A NAME="removeOnImageLoadedListenerHolder(com.google.android.gms.common.images.ImageManager.ListenerHolder)"></A>
-
-<div class="jd-details api apilevel-"> 
-    <h4 class="jd-details-title">
-      <span class="normal">
-        public 
-         
-         
-         
-         
-        void
-      </span>
-      <span class="sympad">removeOnImageLoadedListenerHolder</span>
-      <span class="normal">(ImageManager.ListenerHolder imageViewLoadListener)</span>
-    </h4>
-      <div class="api-level">
-        <div></div>
-        
-  
-
-      </div>
-    <div class="jd-details-descr">
-      
-  <div class="jd-tagdata jd-tagdescr"><p></p></div>
-
-    </div>
-</div>
-
-
-
-
-
-<!-- ========= METHOD DETAIL ======== -->
-
-
-
-<!-- ========= END OF CLASS DATA ========= -->
-<A NAME="navbar_top"></A>
-
-<div id="footer" class="wrap" >
-        
-
-  <div id="copyright">
-    
-  Except as noted, this content is licensed under <a
-  href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>. 
-  For details and restrictions, see the <a href="/license.html">
-  Content License</a>.
-  </div>
-  <div id="build_info">
-    
-<script src="/timestamp.js" type="text/javascript"></script>
-<script>document.write(BUILD_TIMESTAMP)</script>
-
-  </div>
-
-
-  <div id="footerlinks">
-    
-  <p>
-    <a href="/about/index.html">About Android</a>&nbsp;&nbsp;|&nbsp;
-    <a href="/legal.html">Legal</a>&nbsp;&nbsp;|&nbsp;
-    <a href="/support.html">Support</a>
-  </p>
-  </div>
-
-</div> <!-- end footer -->
-</div> <!-- jd-content -->
-
-</div><!-- end doc-content -->
-
-</div> <!-- end body-content --> 
-
-
-
-
-
-
-</body>
-</html>
diff --git a/docs/html/reference/com/google/android/gms/common/images/ImageManager.OnImageLoadedListener.html b/docs/html/reference/com/google/android/gms/common/images/ImageManager.OnImageLoadedListener.html
index efa3a83..c51f59f 100644
--- a/docs/html/reference/com/google/android/gms/common/images/ImageManager.OnImageLoadedListener.html
+++ b/docs/html/reference/com/google/android/gms/common/images/ImageManager.OnImageLoadedListener.html
@@ -679,6 +679,10 @@
 <div class="jd-descr">
 
 
+<h2>Class Overview</h2>
+<p itemprop="articleBody">Listener interface for handling when the image for a particular URI has been loaded.
+</p>
+
 
 
 
@@ -749,6 +753,8 @@
         <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/com/google/android/gms/common/images/ImageManager.OnImageLoadedListener.html#onImageLoaded(android.net.Uri, android.graphics.drawable.Drawable)">onImageLoaded</a></span>(Uri uri, Drawable drawable)</nobr>
         
+        <div class="jd-descrdiv">Listener method invoked when an image has been loaded.</div>
+  
   </td></tr>
 
 
@@ -823,7 +829,21 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p></p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Listener method invoked when an image has been loaded.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>uri</td>
+          <td>The URI of the loaded image.</td>
+        </tr>
+        <tr>
+          <th>drawable</td>
+          <td>Drawable containing the image.
+</td>
+        </tr>
+      </table>
+  </div>
 
     </div>
 </div>
diff --git a/docs/html/reference/com/google/android/gms/common/images/ImageManager.html b/docs/html/reference/com/google/android/gms/common/images/ImageManager.html
index b1d9572..a89fb11 100644
--- a/docs/html/reference/com/google/android/gms/common/images/ImageManager.html
+++ b/docs/html/reference/com/google/android/gms/common/images/ImageManager.html
@@ -767,21 +767,9 @@
          
          
         
-        class</nobr></td>
-      <td class="jd-linkcol"><a href="/reference/com/google/android/gms/common/images/ImageManager.ImageReceiver.html">ImageManager.ImageReceiver</a></td>
-      <td class="jd-descrcol" width="100%">&nbsp;</td>
-    </tr>
-    
-    
-    <tr class=" api apilevel-" >
-      <td class="jd-typecol"><nobr>
-        
-         
-         
-        
         interface</nobr></td>
       <td class="jd-linkcol"><a href="/reference/com/google/android/gms/common/images/ImageManager.OnImageLoadedListener.html">ImageManager.OnImageLoadedListener</a></td>
-      <td class="jd-descrcol" width="100%">&nbsp;</td>
+      <td class="jd-descrcol" width="100%">Listener interface for handling when the image for a particular URI has been loaded.&nbsp;</td>
     </tr>
     
     
@@ -1269,7 +1257,7 @@
         </tr>
         <tr>
           <th>uri</td>
-          <td>Uri to load the image data from.</td>
+          <td>URI to load the image data from.</td>
         </tr>
       </table>
   </div>
@@ -1356,8 +1344,8 @@
  <p>
  The result is delivered to the given listener on the main thread.
  <p>
- If we don't find a result image view will be set to the given default resource if the image
- needs to be loaded asynchronously.</p></div>
+ If a result is not found, the image view will be set to the given default resource if the
+ image needs to be loaded asynchronously.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Parameters</h5>
       <table class="jd-tagtable">
@@ -1367,7 +1355,7 @@
         </tr>
         <tr>
           <th>uri</td>
-          <td>Uri to load the image data from.</td>
+          <td>URI to load the image data from.</td>
         </tr>
         <tr>
           <th>defaultResId</td>
@@ -1417,7 +1405,7 @@
         </tr>
         <tr>
           <th>uri</td>
-          <td>Uri to load the image data from.
+          <td>URI to load the image data from.
 </td>
         </tr>
       </table>
@@ -1471,7 +1459,7 @@
         </tr>
         <tr>
           <th>uri</td>
-          <td>Uri to load the image data from.</td>
+          <td>URI to load the image data from.</td>
         </tr>
         <tr>
           <th>defaultResId</td>
diff --git a/docs/html/reference/com/google/android/gms/common/images/package-summary.html b/docs/html/reference/com/google/android/gms/common/images/package-summary.html
index b5d1582d..b8b6f4a 100644
--- a/docs/html/reference/com/google/android/gms/common/images/package-summary.html
+++ b/docs/html/reference/com/google/android/gms/common/images/package-summary.html
@@ -640,6 +640,11 @@
 <div id="jd-content" class="api apilevel-">
 
 
+  <div class="jd-descr">
+    Contains classes for loading images from Google Play services.
+
+  </div>
+
 
 
 
@@ -651,7 +656,7 @@
   <table class="jd-sumtable-expando">
         <tr class="alt-color api apilevel-" >
               <td class="jd-linkcol"><a href="/reference/com/google/android/gms/common/images/ImageManager.OnImageLoadedListener.html">ImageManager.OnImageLoadedListener</a></td>
-              <td class="jd-descrcol" width="100%">&nbsp;</td>
+              <td class="jd-descrcol" width="100%">Listener interface for handling when the image for a particular URI has been loaded.&nbsp;</td>
           </tr>
   </table>
     </div>
@@ -667,10 +672,6 @@
               <td class="jd-linkcol"><a href="/reference/com/google/android/gms/common/images/ImageManager.html">ImageManager</a></td>
               <td class="jd-descrcol" width="100%">This class is used to load images from the network and handles local caching for you.&nbsp;</td>
           </tr>
-        <tr class=" api apilevel-" >
-              <td class="jd-linkcol"><a href="/reference/com/google/android/gms/common/images/ImageManager.ImageReceiver.html">ImageManager.ImageReceiver</a></td>
-              <td class="jd-descrcol" width="100%">&nbsp;</td>
-          </tr>
   </table>
     </div>
   
diff --git a/docs/html/reference/com/google/android/gms/common/package-summary.html b/docs/html/reference/com/google/android/gms/common/package-summary.html
index 2e43027..b8877eb 100644
--- a/docs/html/reference/com/google/android/gms/common/package-summary.html
+++ b/docs/html/reference/com/google/android/gms/common/package-summary.html
@@ -656,8 +656,7 @@
   <table class="jd-sumtable-expando">
         <tr class="alt-color api apilevel-" >
               <td class="jd-linkcol"><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html">GooglePlayServicesClient</a></td>
-              <td class="jd-descrcol" width="100%">
-&nbsp;</td>
+              <td class="jd-descrcol" width="100%">Base class for clients that connect with Google Play services.&nbsp;</td>
           </tr>
         <tr class=" api apilevel-" >
               <td class="jd-linkcol"><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html">GooglePlayServicesClient.ConnectionCallbacks</a></td>
diff --git a/docs/html/reference/com/google/android/gms/games/Game.html b/docs/html/reference/com/google/android/gms/games/Game.html
index d45ede0..e9f4a6b 100644
--- a/docs/html/reference/com/google/android/gms/games/Game.html
+++ b/docs/html/reference/com/google/android/gms/games/Game.html
@@ -1119,24 +1119,6 @@
   </td></tr>
 
 
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            abstract
-            
-            
-            
-            
-            boolean</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad"><a href="/reference/com/google/android/gms/games/Game.html#isPlayEnabledGame()">isPlayEnabledGame</a></span>()</nobr>
-        
-        <div class="jd-descrdiv">Indicates whether the game is a Google Play-Enabled game.</div>
-  
-  </td></tr>
-
-
 
 </table>
 
@@ -1797,40 +1779,6 @@
 </div>
 
 
-<A NAME="isPlayEnabledGame()"></A>
-
-<div class="jd-details api apilevel-"> 
-    <h4 class="jd-details-title">
-      <span class="normal">
-        public 
-         
-         
-        abstract 
-         
-        boolean
-      </span>
-      <span class="sympad">isPlayEnabledGame</span>
-      <span class="normal">()</span>
-    </h4>
-      <div class="api-level">
-        <div></div>
-        
-  
-
-      </div>
-    <div class="jd-details-descr">
-      
-  <div class="jd-tagdata jd-tagdescr"><p>Indicates whether the game is a Google Play-Enabled game.</p></div>
-  <div class="jd-tagdata">
-      <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>Whether the game is a Google Play-Enabled game.
-</li></ul>
-  </div>
-
-    </div>
-</div>
-
-
 
 
 
diff --git a/docs/html/reference/com/google/android/gms/games/GameBuffer.html b/docs/html/reference/com/google/android/gms/games/GameBuffer.html
index 0791927..61678a6 100644
--- a/docs/html/reference/com/google/android/gms/games/GameBuffer.html
+++ b/docs/html/reference/com/google/android/gms/games/GameBuffer.html
@@ -1278,13 +1278,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the item at the specified position. Note that the objects returned from subsequent
  invocations of this method for the same position may not be identical objects, but will be
  equal in value. In other words:
-
- <pre>
- <code>
- buffer.get(i) == buffer.get(i) may return false.
- buffer.get(i).equals(buffer.get(i)) will return true.
- </code>
- </pre></p></div>
+ <p>
+ <code>buffer.get(i) == buffer.get(i)</code> may return false.
+ <p>
+ <code>buffer.get(i).equals(buffer.get(i))</code> will return true.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>position</td>
+          <td>The position of the item to retrieve.</td>
+        </tr>
+      </table>
+  </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>the item at <code>position</code> in this buffer.
diff --git a/docs/html/reference/com/google/android/gms/games/GameEntity.html b/docs/html/reference/com/google/android/gms/games/GameEntity.html
index df5ef52..82e1ef0 100644
--- a/docs/html/reference/com/google/android/gms/games/GameEntity.html
+++ b/docs/html/reference/com/google/android/gms/games/GameEntity.html
@@ -1271,8 +1271,6 @@
         <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/com/google/android/gms/games/GameEntity.html#isPlayEnabledGame()">isPlayEnabledGame</a></span>()</nobr>
         
-        <div class="jd-descrdiv">Indicates whether the game is a Google Play-Enabled game.</div>
-  
   </td></tr>
 
 
@@ -1899,24 +1897,6 @@
   </td></tr>
 
 
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            abstract
-            
-            
-            
-            
-            boolean</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad"><a href="/reference/com/google/android/gms/games/Game.html#isPlayEnabledGame()">isPlayEnabledGame</a></span>()</nobr>
-        
-        <div class="jd-descrdiv">Indicates whether the game is a Google Play-Enabled game.</div>
-  
-  </td></tr>
-
-
 </table>
   </div>
 </div>
@@ -2746,12 +2726,7 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Indicates whether the game is a Google Play-Enabled game.</p></div>
-  <div class="jd-tagdata">
-      <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>Whether the game is a Google Play-Enabled game.
-</li></ul>
-  </div>
+  <div class="jd-tagdata jd-tagdescr"><p></p></div>
 
     </div>
 </div>
diff --git a/docs/html/reference/com/google/android/gms/games/GamesActivityResultCodes.html b/docs/html/reference/com/google/android/gms/games/GamesActivityResultCodes.html
index d017bb0..d4d7253 100644
--- a/docs/html/reference/com/google/android/gms/games/GamesActivityResultCodes.html
+++ b/docs/html/reference/com/google/android/gms/games/GamesActivityResultCodes.html
@@ -1129,12 +1129,12 @@
  to "leave the room" from the real-time multiplayer "waiting room" screen.
 
  (Note that if the user simply exits the "waiting room" screen by pressing
- Back, that does *not* indicate that the user wants to leave the current room.
+ Back, that does <em>not</em> indicate that the user wants to leave the current room.
  The waiting room screen will return <code><a href="/reference/android/app/Activity.html#RESULT_CANCELED">RESULT_CANCELED</a></code> in that
  case.)</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
-      <ul class="nolist"><li><code><a href="/">ERROR(/GamesClient#getRealTimeWaitingRoomIntent())</a></code></li>
+      <ul class="nolist"><li><code><a href="/reference/com/google/android/gms/games/GamesClient.html#getRealTimeWaitingRoomIntent(com.google.android.gms.games.multiplayer.realtime.Room, int)">getRealTimeWaitingRoomIntent(Room, int)</a></code></li>
       </ul>
   </div>
 
diff --git a/docs/html/reference/com/google/android/gms/games/GamesClient.Builder.html b/docs/html/reference/com/google/android/gms/games/GamesClient.Builder.html
index 307da11..5cfd8ab 100644
--- a/docs/html/reference/com/google/android/gms/games/GamesClient.Builder.html
+++ b/docs/html/reference/com/google/android/gms/games/GamesClient.Builder.html
@@ -1183,16 +1183,16 @@
       <table class="jd-tagtable">
         <tr>
           <th>context</td>
-          <td>the context to use for the connection.</td>
+          <td>The context to use for the connection.</td>
         </tr>
         <tr>
           <th>connectedListener</td>
-          <td>the listener where the results of the asynchronous
+          <td>The listener where the results of the asynchronous
             <code><a href="/reference/com/google/android/gms/games/GamesClient.html#connect()">connect()</a></code> call are delivered.</td>
         </tr>
         <tr>
           <th>connectionFailedListener</td>
-          <td>the listener which will be notified if the connection
+          <td>The listener which will be notified if the connection
             attempt fails.
 </td>
         </tr>
diff --git a/docs/html/reference/com/google/android/gms/games/GamesClient.html b/docs/html/reference/com/google/android/gms/games/GamesClient.html
index 1a107bc..63c4d3a 100644
--- a/docs/html/reference/com/google/android/gms/games/GamesClient.html
+++ b/docs/html/reference/com/google/android/gms/games/GamesClient.html
@@ -852,7 +852,7 @@
         <td class="jd-typecol">int</td>
         <td class="jd-linkcol"><a href="/reference/com/google/android/gms/games/GamesClient.html#MAX_RELIABLE_MESSAGE_LEN">MAX_RELIABLE_MESSAGE_LEN</a></td>
         <td class="jd-descrcol" width="100%">This gives the maximum message size supported via the <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendReliableRealTimeMessage(com.google.android.gms.games.multiplayer.realtime.RealTimeReliableMessageSentListener, byte[], java.lang.String, java.lang.String)">sendReliableRealTimeMessage(RealTimeReliableMessageSentListener, byte[], String, String)</a></code>
- APIs (excluding protocol headers).</td>
+ methods (excluding protocol headers).</td>
     </tr>
     
     
@@ -860,7 +860,7 @@
         <td class="jd-typecol">int</td>
         <td class="jd-linkcol"><a href="/reference/com/google/android/gms/games/GamesClient.html#MAX_UNRELIABLE_MESSAGE_LEN">MAX_UNRELIABLE_MESSAGE_LEN</a></td>
         <td class="jd-descrcol" width="100%">This gives the maximum (unfragmented) message size supported via the
- <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendUnreliableRealTimeMessage(byte[], java.lang.String, java.lang.String)">sendUnreliableRealTimeMessage(byte[], String, String)</a></code> APIs (excluding protocol headers).</td>
+ <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendUnreliableRealTimeMessage(byte[], java.lang.String, java.lang.String)">sendUnreliableRealTimeMessage(byte[], String, String)</a></code> methods (excluding protocol headers).</td>
     </tr>
     
     
@@ -934,8 +934,8 @@
     <tr class=" api apilevel-" >
         <td class="jd-typecol">int</td>
         <td class="jd-linkcol"><a href="/reference/com/google/android/gms/games/GamesClient.html#STATUS_INVALID_REAL_TIME_ROOM_ID">STATUS_INVALID_REAL_TIME_ROOM_ID</a></td>
-        <td class="jd-descrcol" width="100%">Constant indicating that real-time room ID provided by the user is not a valid or it is
- not currently active real-time room.</td>
+        <td class="jd-descrcol" width="100%">Constant indicating that the real-time room ID provided to the operation was not valid, or
+ does not correspond to the currently active real-time room.</td>
     </tr>
     
     
@@ -1025,8 +1025,8 @@
     <tr class=" api apilevel-" >
         <td class="jd-typecol">int</td>
         <td class="jd-linkcol"><a href="/reference/com/google/android/gms/games/GamesClient.html#STATUS_REAL_TIME_MESSAGE_FAILED">STATUS_REAL_TIME_MESSAGE_FAILED</a></td>
-        <td class="jd-descrcol" width="100%">Status code returned from <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendUnreliableRealTimeMessage(byte[], java.lang.String, java.lang.String)">sendUnreliableRealTimeMessage(byte[], String, String)</a></code> and the
- <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendReliableRealTimeMessage(com.google.android.gms.games.multiplayer.realtime.RealTimeReliableMessageSentListener, byte[], java.lang.String, java.lang.String)">sendReliableRealTimeMessage(RealTimeReliableMessageSentListener, byte[], String, String)</a></code> APIs when the message send operation failed due to an
+        <td class="jd-descrcol" width="100%">Status code returned from the <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendUnreliableRealTimeMessage(byte[], java.lang.String, java.lang.String)">sendUnreliableRealTimeMessage(byte[], String, String)</a></code> and
+ <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendReliableRealTimeMessage(com.google.android.gms.games.multiplayer.realtime.RealTimeReliableMessageSentListener, byte[], java.lang.String, java.lang.String)">sendReliableRealTimeMessage(RealTimeReliableMessageSentListener, byte[], String, String)</a></code> methods when the message send operation failed due to an
  immediate error.</td>
     </tr>
     
@@ -1275,6 +1275,22 @@
             
             
             
+            <a href="/reference/com/google/android/gms/games/Game.html">Game</a></nobr>
+        </td>
+        <td class="jd-linkcol" width="100%"><nobr>
+        <span class="sympad"><a href="/reference/com/google/android/gms/games/GamesClient.html#getCurrentGame()">getCurrentGame</a></span>()</nobr>
+        
+  </td></tr>
+
+
+	 
+    <tr class="alt-color api apilevel-" >
+        <td class="jd-typecol"><nobr>
+            
+            
+            
+            
+            
             <a href="/reference/com/google/android/gms/games/Player.html">Player</a></nobr>
         </td>
         <td class="jd-linkcol" width="100%"><nobr>
@@ -1284,7 +1300,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1300,7 +1316,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1318,7 +1334,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1336,7 +1352,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1354,7 +1370,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1373,7 +1389,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1391,7 +1407,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1410,7 +1426,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1428,7 +1444,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1446,7 +1462,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1465,7 +1481,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1483,7 +1499,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1502,7 +1518,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1521,7 +1537,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1539,7 +1555,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1557,7 +1573,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1575,7 +1591,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1593,7 +1609,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1612,7 +1628,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1630,7 +1646,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1648,7 +1664,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1666,7 +1682,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1684,7 +1700,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1702,7 +1718,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1720,7 +1736,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1738,7 +1754,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1756,7 +1772,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1774,7 +1790,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1792,7 +1808,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1810,7 +1826,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1828,7 +1844,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1847,7 +1863,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1865,7 +1881,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1883,7 +1899,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1901,7 +1917,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1919,7 +1935,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1937,7 +1953,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1955,7 +1971,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1973,7 +1989,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1992,7 +2008,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2011,7 +2027,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2029,7 +2045,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2047,7 +2063,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2065,7 +2081,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2083,7 +2099,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2101,7 +2117,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2119,7 +2135,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2137,7 +2153,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2155,7 +2171,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2173,7 +2189,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2910,7 +2926,7 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>This gives the maximum message size supported via the <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendReliableRealTimeMessage(com.google.android.gms.games.multiplayer.realtime.RealTimeReliableMessageSentListener, byte[], java.lang.String, java.lang.String)">sendReliableRealTimeMessage(RealTimeReliableMessageSentListener, byte[], String, String)</a></code>
- APIs (excluding protocol headers).
+ methods (excluding protocol headers).
 </p></div>
 
     
@@ -2950,7 +2966,7 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>This gives the maximum (unfragmented) message size supported via the
- <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendUnreliableRealTimeMessage(byte[], java.lang.String, java.lang.String)">sendUnreliableRealTimeMessage(byte[], String, String)</a></code> APIs (excluding protocol headers).
+ <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendUnreliableRealTimeMessage(byte[], java.lang.String, java.lang.String)">sendUnreliableRealTimeMessage(byte[], String, String)</a></code> methods (excluding protocol headers).
 </p></div>
 
     
@@ -3342,8 +3358,8 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Constant indicating that real-time room ID provided by the user is not a valid or it is
- not currently active real-time room.
+  <div class="jd-tagdata jd-tagdescr"><p>Constant indicating that the real-time room ID provided to the operation was not valid, or
+ does not correspond to the currently active real-time room.
 </p></div>
 
     
@@ -3820,8 +3836,8 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Status code returned from <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendUnreliableRealTimeMessage(byte[], java.lang.String, java.lang.String)">sendUnreliableRealTimeMessage(byte[], String, String)</a></code> and the
- <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendReliableRealTimeMessage(com.google.android.gms.games.multiplayer.realtime.RealTimeReliableMessageSentListener, byte[], java.lang.String, java.lang.String)">sendReliableRealTimeMessage(RealTimeReliableMessageSentListener, byte[], String, String)</a></code> APIs when the message send operation failed due to an
+  <div class="jd-tagdata jd-tagdescr"><p>Status code returned from the <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendUnreliableRealTimeMessage(byte[], java.lang.String, java.lang.String)">sendUnreliableRealTimeMessage(byte[], String, String)</a></code> and
+ <code><a href="/reference/com/google/android/gms/games/GamesClient.html#sendReliableRealTimeMessage(com.google.android.gms.games.multiplayer.realtime.RealTimeReliableMessageSentListener, byte[], java.lang.String, java.lang.String)">sendReliableRealTimeMessage(RealTimeReliableMessageSentListener, byte[], String, String)</a></code> methods when the message send operation failed due to an
  immediate error.
 </p></div>
 
@@ -4095,7 +4111,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>config</td>
-          <td>the real-time room configuration.
+          <td>The real-time room configuration.
 </td>
         </tr>
       </table>
@@ -4134,7 +4150,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>invitationId</td>
-          <td>the ID of the invitation to decline.
+          <td>The ID of the invitation to decline.
 </td>
         </tr>
       </table>
@@ -4204,7 +4220,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>invitationId</td>
-          <td>the ID of the invitation to dismiss.
+          <td>The ID of the invitation to dismiss.
 </td>
         </tr>
       </table>
@@ -4246,7 +4262,7 @@
  state.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>an <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to view the currently signed in player's
+      <ul class="nolist"><li>An <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to view the currently signed in player's
          achievements.
 </li></ul>
   </div>
@@ -4287,7 +4303,7 @@
  state.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>an <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to view the list of leaderboards for a game.
+      <ul class="nolist"><li>An <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to view the list of leaderboards for a game.
 </li></ul>
   </div>
 
@@ -4378,6 +4394,41 @@
 </div>
 
 
+<A NAME="getCurrentGame()"></A>
+
+<div class="jd-details api apilevel-"> 
+    <h4 class="jd-details-title">
+      <span class="normal">
+        public 
+         
+         
+         
+         
+        <a href="/reference/com/google/android/gms/games/Game.html">Game</a>
+      </span>
+      <span class="sympad">getCurrentGame</span>
+      <span class="normal">()</span>
+    </h4>
+      <div class="api-level">
+        <div></div>
+        
+  
+
+      </div>
+    <div class="jd-details-descr">
+      
+  <div class="jd-tagdata jd-tagdescr"><p></p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li><code><a href="/reference/com/google/android/gms/games/Game.html">Game</a></code> metadata for the current game. May be null if the metadata is not
+         available locally.
+</li></ul>
+  </div>
+
+    </div>
+</div>
+
+
 <A NAME="getCurrentPlayer()"></A>
 
 <div class="jd-details api apilevel-"> 
@@ -4438,7 +4489,7 @@
   <div class="jd-tagdata jd-tagdescr"><p></p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the player ID for the currently signed in player.
+      <ul class="nolist"><li>The player ID for the currently signed in player.
 </li></ul>
   </div>
 
@@ -4473,13 +4524,13 @@
  that this must be invoked using <code><a href="/reference/android/app/Activity.html#startActivityForResult(android.content.Intent, int)">startActivityForResult(Intent, int)</a></code> so that
  the identity of the calling package can be established.
  <p>
- If the user canceled the result will be <code><a href="/reference/android/app/Activity.html#RESULT_CANCELED">RESULT_CANCELED</a></code>. If the user
+ If the user canceled, the result will be <code><a href="/reference/android/app/Activity.html#RESULT_CANCELED">RESULT_CANCELED</a></code>. If the user
  selected an invitation to accept, the result will be <code><a href="/reference/android/app/Activity.html#RESULT_OK">RESULT_OK</a></code> and the data
  intent will contain the selected invitation as a parcelable extra in
  <code><a href="/reference/com/google/android/gms/games/GamesClient.html#EXTRA_INVITATION">EXTRA_INVITATION</a></code>.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>an <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to view the invitation inbox UI.
+      <ul class="nolist"><li>An <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to view the invitation inbox UI.
 </li></ul>
   </div>
 
@@ -4528,7 +4579,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>an <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to view the specified leaderboard.
+      <ul class="nolist"><li>An <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to view the specified leaderboard.
 </li></ul>
   </div>
 
@@ -4573,13 +4624,13 @@
         </tr>
         <tr>
           <th>participantId</td>
-          <td>the ID of the participant to whom this socket is bound</td>
+          <td>The ID of the participant to whom this socket is bound</td>
         </tr>
       </table>
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>instance of a LocalSocket, or null on error.
+      <ul class="nolist"><li>An instance of a LocalSocket, or null on error.
 </li></ul>
   </div>
 
@@ -4626,8 +4677,18 @@
  data intent containing a <code><a href="/reference/com/google/android/gms/games/multiplayer/realtime/Room.html">Room</a></code> object in <code><a href="/reference/com/google/android/gms/games/GamesClient.html#EXTRA_ROOM">EXTRA_ROOM</a></code> that represents
  the current state of the Room that you originally passed as a parameter here.
  <p>
- Note that the waiting room itself will never explicitly take any action to change
- the state of the room or its participants.  So if the activity result is
+ If desired, the waiting room can allow the user to start playing the game even before
+ the room is fully connected.  This is controlled by the <code>minParticipantsToStart</code>
+ parameter: if at least that many participants (including the current player) are
+ connected to the room, a "Start playing" menu item will become enabled in the waiting
+ room UI.  Setting <code>minParticipantsToStart</code> to 0 means that "Start playing" will
+ always be available, and a value of <code><a href="/reference/java/lang/Integer.html#MAX_VALUE">MAX_VALUE</a></code> will disable the item
+ completely.  Note: if you do allow the user to start early, you'll need to handle that
+ situation by explicitly telling the other connected peers that the game is now starting;
+ see the developer documentation for more details.
+ <p>
+ Finally, note that the waiting room itself will never explicitly take any action to
+ change the state of the room or its participants.  So if the activity result is
  <code><a href="/reference/com/google/android/gms/games/GamesActivityResultCodes.html#RESULT_LEFT_ROOM">RESULT_LEFT_ROOM</a></code>, it's the caller's responsibility
  to actually leave the room.  Or if the result is <code><a href="/reference/android/app/Activity.html#RESULT_CANCELED">RESULT_CANCELED</a></code>,
  it's the responsibility of the caller to double-check the current state of the Room
@@ -4644,21 +4705,15 @@
         </tr>
         <tr>
           <th>minParticipantsToStart</td>
-          <td>the minimum number of participants that must
-            be connected to the room (including the current player) before the
-            "Start playing" option becomes enabled.
-            <p>
-            If this is 0, that means that "Start playing" will always be enabled.
-            If it's equal to the maximum possible number of participants (including
-            the current player <i>and</i> any auto-match participants) then the
-            'Start playing' option will never be enabled; instead the waiting room
-            will exit automatically as soon as all participants are fully connected.</td>
+          <td>the minimum number of participants that must be
+            connected to the room (including the current player) for the "Start
+            playing" menu item to become enabled.</td>
         </tr>
       </table>
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>an <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to display the waiting room screen.</li></ul>
+      <ul class="nolist"><li>An <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to display the waiting room screen.</li></ul>
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
@@ -4722,7 +4777,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>an <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to display the player selector.</li></ul>
+      <ul class="nolist"><li>An <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to display the player selector.</li></ul>
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">See Also</h5>
@@ -4771,7 +4826,7 @@
  menu item.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>an <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to view the GamesClient Settings UI.
+      <ul class="nolist"><li>An <code><a href="/reference/android/content/Intent.html">Intent</a></code> that can be started to view the GamesClient Settings UI.
 </li></ul>
   </div>
 
@@ -4817,11 +4872,11 @@
       <table class="jd-tagtable">
         <tr>
           <th>id</td>
-          <td>the achievement ID to increment.</td>
+          <td>The achievement ID to increment.</td>
         </tr>
         <tr>
           <th>numSteps</td>
-          <td>the number of steps to increment by. Must be greater than 0.
+          <td>The number of steps to increment by. Must be greater than 0.
 </td>
         </tr>
       </table>
@@ -4869,16 +4924,16 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the increment achievement is complete. The
+          <td>The listener that is called when the increment achievement is complete. The
             listener is called on the main thread.</td>
         </tr>
         <tr>
           <th>id</td>
-          <td>the ID of the achievement to increment.</td>
+          <td>The ID of the achievement to increment.</td>
         </tr>
         <tr>
           <th>numSteps</td>
-          <td>the number of steps to increment by. Must be greater than 0.
+          <td>The number of steps to increment by. Must be greater than 0.
 </td>
         </tr>
       </table>
@@ -4916,7 +4971,7 @@
  client actions caused by the user with a call to this method.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is connected to the service.
+      <ul class="nolist"><li>true if the client is connected to the service.
 </li></ul>
   </div>
 
@@ -4950,7 +5005,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Checks if the client is attempting to connect to the service.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is attempting to connect to the service.
+      <ul class="nolist"><li>true if the client is attempting to connect to the service.
 </li></ul>
   </div>
 
@@ -4994,7 +5049,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              events.</li></ul>
   </div>
 
@@ -5038,7 +5093,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              failed events.</li></ul>
   </div>
 
@@ -5080,7 +5135,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>config</td>
-          <td>the real-time room configuration.
+          <td>The real-time room configuration.
 </td>
         </tr>
       </table>
@@ -5121,7 +5176,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is notified after the room has been left. The listener is
+          <td>The listener that is notified after the room has been left. The listener is
             called on the main thread.</td>
         </tr>
         <tr>
@@ -5168,7 +5223,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.
 </td>
         </tr>
@@ -5208,7 +5263,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.
 </td>
         </tr>
@@ -5252,12 +5307,12 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.</td>
         </tr>
         <tr>
           <th>pageSize</td>
-          <td>the number of entries to request for this initial page. Note that if cached
+          <td>The number of entries to request for this initial page. Note that if cached
             data already exists, the returned buffer may contain more than this size, but it
             is guaranteed to contain at least this many if the collection contains enough
             records. This must be a value between 1 and 25.</td>
@@ -5309,7 +5364,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.
 </td>
         </tr>
@@ -5352,7 +5407,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.</td>
         </tr>
         <tr>
@@ -5399,7 +5454,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.
 </td>
         </tr>
@@ -5443,12 +5498,12 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.</td>
         </tr>
         <tr>
           <th>pageSize</td>
-          <td>the number of additional entries to request. This must be a value between 1
+          <td>The number of additional entries to request. This must be a value between 1
             and 25.
 </td>
         </tr>
@@ -5492,12 +5547,12 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.</td>
         </tr>
         <tr>
           <th>buffer</td>
-          <td>the existing buffer that will be expanded.</td>
+          <td>The existing buffer that will be expanded.</td>
         </tr>
         <tr>
           <th>maxResults</td>
@@ -5505,8 +5560,8 @@
         </tr>
         <tr>
           <th>pageDirection</td>
-          <td>the direction to expand the buffer. Values are defined in
-            <code><a href="/reference/com/google/android/gms/games/PageDirection.html">PageDirection</a></code>
+          <td>The direction to expand the buffer. Values are defined in
+            <code><a href="/reference/com/google/android/gms/games/PageDirection.html">PageDirection</a></code>.
 </td>
         </tr>
       </table>
@@ -5548,12 +5603,12 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.</td>
         </tr>
         <tr>
           <th>playerId</td>
-          <td>the player ID to get full profile data for.
+          <td>The player ID to get full profile data for.
 </td>
         </tr>
       </table>
@@ -5596,7 +5651,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.</td>
         </tr>
         <tr>
@@ -5668,7 +5723,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.</td>
         </tr>
         <tr>
@@ -5732,7 +5787,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the load is complete. The listener is called
+          <td>The listener that is called when the load is complete. The listener is called
             on the main thread.</td>
         </tr>
         <tr>
@@ -5901,9 +5956,10 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection events from this <code>GooglePlayServicesClient</code>.
- If we are already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code> method
- will be called immediately.  Applications should balance calls to this method with calls to
- <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking resources.
+ If the service is already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code>
+ method will be called immediately.  Applications should balance calls to this method with
+ calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection events, this
  method will not add a duplicate entry for the same listener, but <strong>will</strong>
@@ -5952,11 +6008,12 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection failed events from this
- <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if we are not
- already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code>
- method will not be called immediately.  Applications should balance calls to this method with
- calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid
- leaking resources.
+ <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if the service
+ is not already connected, the listener's
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code> method will not be called immediately.
+ Applications should balance calls to this method with calls to
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection failed events, this
  method will not add a duplicate entry for the same listener.
@@ -6013,7 +6070,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when a new invitation is received. The listener
+          <td>The listener that is called when a new invitation is received. The listener
             is called on the main thread.
 </td>
         </tr>
@@ -6061,7 +6118,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>id</td>
-          <td>the achievement ID to reveal</td>
+          <td>The achievement ID to reveal</td>
         </tr>
       </table>
   </div>
@@ -6112,12 +6169,12 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the reveal achievement is complete.
+          <td>The listener that is called when the reveal achievement is complete.
            The listener is called on the main thread.</td>
         </tr>
         <tr>
           <th>id</td>
-          <td>the ID of the achievement to reveal</td>
+          <td>The ID of the achievement to reveal</td>
         </tr>
       </table>
   </div>
@@ -6164,11 +6221,11 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is notified when the message has been sent.</td>
+          <td>The listener that is notified when the message has been sent.</td>
         </tr>
         <tr>
           <th>messageData</td>
-          <td>the message to be sent. Should be at most
+          <td>The message to be sent. Should be at most
             <code><a href="/reference/com/google/android/gms/games/GamesClient.html#MAX_RELIABLE_MESSAGE_LEN">MAX_RELIABLE_MESSAGE_LEN</a></code> bytes.</td>
         </tr>
         <tr>
@@ -6177,16 +6234,15 @@
         </tr>
         <tr>
           <th>recipientParticipantId</td>
-          <td>the participant ID to send the message to.</td>
+          <td>The participant ID to send the message to.</td>
         </tr>
       </table>
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>token for the message sent, which is returned in callback
+      <ul class="nolist"><li>The token for the message sent, which is returned in callback
          <code><a href="/reference/com/google/android/gms/games/multiplayer/realtime/RealTimeReliableMessageSentListener.html#onRealTimeMessageSent(int, int, java.lang.String)">onRealTimeMessageSent(int, int, String)</a></code> or
          <code><a href="/reference/com/google/android/gms/games/GamesClient.html#STATUS_REAL_TIME_MESSAGE_FAILED">STATUS_REAL_TIME_MESSAGE_FAILED</a></code> if the message failed to send.
-
 </li></ul>
   </div>
 
@@ -6227,7 +6283,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>messageData</td>
-          <td>the message to be sent. Should be at most
+          <td>The message to be sent. Should be at most
             <code><a href="/reference/com/google/android/gms/games/GamesClient.html#MAX_UNRELIABLE_MESSAGE_LEN">MAX_UNRELIABLE_MESSAGE_LEN</a></code> bytes.</td>
         </tr>
         <tr>
@@ -6284,7 +6340,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>messageData</td>
-          <td>the message to be sent. Should be at most
+          <td>The message to be sent. Should be at most
             <code><a href="/reference/com/google/android/gms/games/GamesClient.html#MAX_UNRELIABLE_MESSAGE_LEN">MAX_UNRELIABLE_MESSAGE_LEN</a></code> bytes.</td>
         </tr>
         <tr>
@@ -6293,7 +6349,7 @@
         </tr>
         <tr>
           <th>recipientParticipantId</td>
-          <td>the participant ID to send the message to.</td>
+          <td>The participant ID to send the message to.</td>
         </tr>
       </table>
   </div>
@@ -6339,7 +6395,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>messageData</td>
-          <td>the message to be sent. Should be at most
+          <td>The message to be sent. Should be at most
             <code><a href="/reference/com/google/android/gms/games/GamesClient.html#MAX_UNRELIABLE_MESSAGE_LEN">MAX_UNRELIABLE_MESSAGE_LEN</a></code> bytes.</td>
         </tr>
         <tr>
@@ -6512,7 +6568,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when sign-out is complete. The listener is called
+          <td>The listener that is called when sign-out is complete. The listener is called
             on the main thread.
 </td>
         </tr>
@@ -6597,8 +6653,8 @@
  </ul>
  <p>
  For more details, please see <a
- href="https://developers.google.com/games/services/common/concepts/leaderboards">this
- page</a>.</p></div>
+ href="https://developers.google.com/games/services/common/concepts/leaderboards">Leaderboard
+ Concepts</a>.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Parameters</h5>
       <table class="jd-tagtable">
@@ -6731,7 +6787,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>id</td>
-          <td>the achievement ID to unlock</td>
+          <td>The achievement ID to unlock</td>
         </tr>
       </table>
   </div>
@@ -6782,12 +6838,12 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called when the unlock achievement is complete. The
+          <td>The listener that is called when the unlock achievement is complete. The
             listener is called on the main thread.</td>
         </tr>
         <tr>
           <th>id</td>
-          <td>the ID of the achievement to unlock.</td>
+          <td>The ID of the achievement to unlock.</td>
         </tr>
       </table>
   </div>
diff --git a/docs/html/reference/com/google/android/gms/games/GamesClientSettings.html b/docs/html/reference/com/google/android/gms/games/GamesClientSettings.html
deleted file mode 100644
index 29cad90..0000000
--- a/docs/html/reference/com/google/android/gms/games/GamesClientSettings.html
+++ /dev/null
@@ -1,1189 +0,0 @@
-<!DOCTYPE html>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-<html>
-<head>
-
-
-
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
-
-<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
-<title>GamesClientSettings | Android Developers</title>
-
-<!-- STYLESHEETS -->
-<link rel="stylesheet"
-href="//fonts.googleapis.com/css?family=Roboto:regular,medium,thin,italic,mediumitalic,bold" title="roboto">
-<link href="/assets/css/default.css" rel="stylesheet" type="text/css">
-
-
-
-<!-- JAVASCRIPT -->
-<script src="//www.google.com/jsapi" type="text/javascript"></script>
-<script src="/assets/js/android_3p-bundle.js" type="text/javascript"></script>
-<script type="text/javascript">
-  var toRoot = "/";
-  var devsite = false;
-</script>
-<script src="/assets/js/docs.js" type="text/javascript"></script>
-
-<script type="text/javascript">
-  var _gaq = _gaq || [];
-  _gaq.push(['_setAccount', 'UA-5831155-1']);
-  _gaq.push(['_trackPageview']);
-
-  (function() {
-    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
-    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
-    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
-  })();
-</script>
-</head>
-<body class="gc-documentation google
-  develop" itemscope itemtype="http://schema.org/Article">
-  <div id="doc-api-level" class="" style="display:none"></div>
-  <a name="top"></a>
-
-<a name="top"></a>
-
-    <!-- Header -->
-    <div id="header">
-        <div class="wrap" id="header-wrap">
-          <div class="col-3 logo">
-          <a href="/index.html">
-            <img src="/assets/images/dac_logo.png" width="123" height="25" alt="Android Developers" />
-          </a>
-          <div class="btn-quicknav" id="btn-quicknav">
-          	<a href="#" class="arrow-inactive">Quicknav</a>
-			      <a href="#" class="arrow-active">Quicknav</a>
-          </div>
-          </div>
-            <ul class="nav-x col-9">
-                <li class="design">
-                  <a href="/design/index.html"
-                  zh-tw-lang="設計"
-                  zh-cn-lang="设计"
-                  ru-lang="Проектирование"
-                  ko-lang="디자인"
-                  ja-lang="設計"
-                  es-lang="Diseñar"               
-                  >Design</a></li>
-                <li class="develop"><a href="/develop/index.html"
-                  zh-tw-lang="開發"
-                  zh-cn-lang="开发"
-                  ru-lang="Разработка"
-                  ko-lang="개발"
-                  ja-lang="開発"
-                  es-lang="Desarrollar"               
-                  >Develop</a></li>
-                <li class="distribute last"><a href="/distribute/index.html"
-                  zh-tw-lang="發佈"
-                  zh-cn-lang="分发"
-                  ru-lang="Распространение"
-                  ko-lang="배포"
-                  ja-lang="配布"
-                  es-lang="Distribuir"               
-                  >Distribute</a></li>
-            </ul>
-            
-            <!-- New Search -->
-            <div class="menu-container">
-            <div class="moremenu">
-    <div id="more-btn"></div>
-  </div>
-  <div class="morehover" id="moremenu">
-    <div class="top"></div>
-    <div class="mid">
-      <div class="header">Links</div>
-      <ul>
-        <li><a href="https://play.google.com/apps/publish/">Google Play Developer Console</a></li>
-        <li><a href="http://android-developers.blogspot.com/">Android Developers Blog</a></li>
-        <li><a href="/about/index.html">About Android</a></li>
-      </ul>
-      <div class="header">Android Sites</div>
-      <ul>
-        <li><a href="http://www.android.com">Android.com</a></li>
-        <li class="active"><a>Android Developers</a></li>
-        <li><a href="http://source.android.com">Android Open Source Project</a></li>
-      </ul>
-      
-      
-      
-        <div class="header">Language</div>
-          <div id="language" class="locales">
-            <select name="language" onChange="changeLangPref(this.value, true)">
-                <option value="en">English</option>
-                <option value="es">Español</option>
-                <option value="ja">日本語</option>
-                <option value="ko">한국어</option>
-                <option value="ru">Русский</option>
-                <option value="zh-cn">中文 (中国)</option>
-                <option value="zh-tw">中文 (台灣)</option>
-            </select>
-          </div>
-        <script type="text/javascript">
-          <!--
-          loadLangPref();
-            //-->
-        </script>
-      
-      
-
-
-      <br class="clearfix" />
-    </div>
-    <div class="bottom"></div>
-  </div>
-  <div class="search" id="search-container">
-    <div class="search-inner">
-      <div id="search-btn"></div>
-      <div class="left"></div>
-      <form onsubmit="return submit_search()">
-        <input id="search_autocomplete" type="text" value="" autocomplete="off" name="q"
-onfocus="search_focus_changed(this, true)" onblur="search_focus_changed(this, false)"
-onkeydown="return search_changed(event, true, '/')" 
-onkeyup="return search_changed(event, false, '/')" />
-      </form>
-      <div class="right"></div>
-        <a class="close hide">close</a>
-        <div class="left"></div>
-        <div class="right"></div>
-    </div>
-  </div>
-
-  <div class="search_filtered_wrapper reference">
-    <div class="suggest-card reference no-display">
-      <ul class="search_filtered">
-      </ul>
-    </div>
-  </div>
-
-  <div class="search_filtered_wrapper docs">
-    <div class="suggest-card dummy no-display">&nbsp;</div>
-    <div class="suggest-card develop no-display">
-      <ul class="search_filtered">
-      </ul>
-      <div class="child-card guides no-display">
-      </div>
-      <div class="child-card training no-display">
-      </div>
-    </div>
-    <div class="suggest-card design no-display">
-      <ul class="search_filtered">
-      </ul>
-    </div>
-    <div class="suggest-card distribute no-display">
-      <ul class="search_filtered">
-      </ul>
-    </div>
-  </div>
-
-  </div>
-  <!-- /New Search>
-          
-          
-          <!-- Expanded quicknav -->
-           <div id="quicknav" class="col-9">
-                <ul>
-                    <li class="design">
-                      <ul>
-                        <li><a href="/design/index.html">Get Started</a></li>
-                        <li><a href="/design/style/index.html">Style</a></li>
-                        <li><a href="/design/patterns/index.html">Patterns</a></li>
-                        <li><a href="/design/building-blocks/index.html">Building Blocks</a></li>
-                        <li><a href="/design/downloads/index.html">Downloads</a></li>
-                        <li><a href="/design/videos/index.html">Videos</a></li>
-                      </ul>
-                    </li>
-                    <li class="develop">
-                      <ul>
-                        <li><a href="/training/index.html"
-                          zh-tw-lang="訓練課程"
-                          zh-cn-lang="培训"
-                          ru-lang="Курсы"
-                          ko-lang="교육"
-                          ja-lang="トレーニング"
-                          es-lang="Capacitación"               
-                          >Training</a></li>
-                        <li><a href="/guide/components/index.html"
-                          zh-tw-lang="API 指南"
-                          zh-cn-lang="API 指南"
-                          ru-lang="Руководства по API"
-                          ko-lang="API 가이드"
-                          ja-lang="API ガイド"
-                          es-lang="Guías de la API"               
-                          >API Guides</a></li>
-                        <li><a href="/reference/packages.html"
-                          zh-tw-lang="參考資源"
-                          zh-cn-lang="参考"
-                          ru-lang="Справочник"
-                          ko-lang="참조문서"
-                          ja-lang="リファレンス"
-                          es-lang="Referencia"               
-                          >Reference</a></li>
-                        <li><a href="/tools/index.html"
-                          zh-tw-lang="相關工具"
-                          zh-cn-lang="工具"
-                          ru-lang="Инструменты"
-                          ko-lang="도구"
-                          ja-lang="ツール"
-                          es-lang="Herramientas"               
-                          >Tools</a>
-                          <ul><li><a href="/sdk/index.html">Get the SDK</a></li></ul>
-                        </li>
-                        <li><a href="/google/index.html">Google Services</a>
-                        </li>
-                      </ul>
-                    </li>
-                    <li class="distribute last">
-                      <ul>
-                        <li><a href="/distribute/index.html">Google Play</a></li>
-                        <li><a href="/distribute/googleplay/publish/index.html">Publishing</a></li>
-                        <li><a href="/distribute/googleplay/promote/index.html">Promoting</a></li>
-                        <li><a href="/distribute/googleplay/quality/index.html">App Quality</a></li>
-                        <li><a href="/distribute/googleplay/spotlight/index.html">Spotlight</a></li>
-                        <li><a href="/distribute/open.html">Open Distribution</a></li>
-                      </ul>
-                    </li>
-                </ul>
-          </div>
-          <!-- /Expanded quicknav -->
-        </div>
-    </div>
-    <!-- /Header -->
-    
-    
-  <div id="searchResults" class="wrap" style="display:none;">
-          <h2 id="searchTitle">Results</h2>
-          <div id="leftSearchControl" class="search-control">Loading...</div>
-  </div>
-    
-    
-  
-    <!-- Secondary x-nav -->
-    <div id="nav-x">
-        <div class="wrap">
-            <ul class="nav-x col-9 develop" style="width:100%">
-                <li class="training"><a href="/training/index.html"
-                  zh-tw-lang="訓練課程"
-                  zh-cn-lang="培训"
-                  ru-lang="Курсы"
-                  ko-lang="교육"
-                  ja-lang="トレーニング"
-                  es-lang="Capacitación"               
-                  >Training</a></li>
-                <li class="guide"><a href="/guide/components/index.html"
-                  zh-tw-lang="API 指南"
-                  zh-cn-lang="API 指南"
-                  ru-lang="Руководства по API"
-                  ko-lang="API 가이드"
-                  ja-lang="API ガイド"
-                  es-lang="Guías de la API"               
-                  >API Guides</a></li>
-                <li class="reference"><a href="/reference/packages.html"
-                  zh-tw-lang="參考資源"
-                  zh-cn-lang="参考"
-                  ru-lang="Справочник"
-                  ko-lang="참조문서"
-                  ja-lang="リファレンス"
-                  es-lang="Referencia"               
-                  >Reference</a></li>
-                <li class="tools"><a href="/tools/index.html"
-                  zh-tw-lang="相關工具"
-                  zh-cn-lang="工具"
-                  ru-lang="Инструменты"
-                  ko-lang="도구"
-                  ja-lang="ツール"
-                  es-lang="Herramientas"
-                  >Tools</a></li>
-                <li class="google"><a href="/google/index.html"
-                  >Google Services</a>
-                </li>
-            </ul>
-        </div>
-        
-    </div>
-    <!-- /Sendondary x-nav -->
-  
-
-
-
-
-  
-
-
-  
-  <div class="wrap clearfix" id="body-content">
-    <div class="col-4" id="side-nav" itemscope itemtype="http://schema.org/SiteNavigationElement">
-      <div id="devdoc-nav" class="scroll-pane">
-<a class="totop" href="#top" data-g-event="left-nav-top">to top</a>
-
-
-
-<ul id="nav">
-
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/index.html">
-          <span class="en">Overview</span>
-      </a></div>
-  </li>
-
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/play-services/games.html">
-          <span class="en">Games</span>
-      </a></div>
-  </li>
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/play-services/location.html">
-          <span class="en">Location</span>
-      </a></div>
-  </li>
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/play-services/plus.html">
-          <span class="en">Google+</span>
-                </a></div>
-  </li>
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/play-services/maps.html">
-          <span class="en">Google Maps</span>
-      </a></div>
-  </li>
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="/google/play-services/auth.html">
-          <span class="en">Authorization</span>
-      </a></div>
-  </li>
-
-
-
-  <li class="nav-section">
-    <div class="nav-section-header"><a href="/google/play-services/index.html">
-      <span class="en">Google Play Services</span></a>
-    </div>
-    <ul>
-      <li><a href="/google/play-services/setup.html">
-          <span class="en">Setup</span></a>
-      </li>
-      <li id="gms-tree-list" class="nav-section">
-        <div class="nav-section-header">
-          <a href="/reference/gms-packages.html">
-            <span class="en">Reference</span>
-          </a>
-        <div>
-      </li>
-    </ul>
-  </li>
-
-
-  <li class="nav-section">
-    <div class="nav-section-header"><a href="/google/play/billing/index.html">
-      <span class="en">Google Play In-app Billing</span></a>
-    </div>
-    <ul>
-      <li><a href="/google/play/billing/billing_overview.html">
-              <span class="en">Overview</span></a>
-      </li>
-      <li class="nav-section"><div class="nav-section-header"><a href="/google/play/billing/api.html">
-              <span class="en">Version 3 API</span></a></div>
-              <ul>
-              <li><a href="/google/play/billing/billing_integrate.html">
-              <span class="en">Implementing the API</span></a></li>
-              <li><a href="/google/play/billing/billing_reference.html">
-              <span class="en">Reference</span></a></li>
-              </ul>
-      </li>
-      <li class="nav-section"><div class="nav-section-header"><a href="/google/play/billing/v2/api.html">
-              <span class="en">Version 2 API</span></a></div>
-              <ul>
-              <li><a href="/google/play/billing/v2/billing_integrate.html">
-              <span class="en">Implementing the API</span></a></li>
-              <li><a href="/google/play/billing/v2/billing_subscriptions.html">
-              <span class="en">Subscriptions</span></a></li>
-              <li><a href="/google/play/billing/v2/billing_reference.html">
-              <span class="en">Reference</span></a></li>
-              </ul>
-      </li>
-      <li><a href="/google/play/billing/billing_subscriptions.html">
-              <span class="en">Subscriptions</span></a>
-      </li>
-      <li><a href="/google/play/billing/billing_best_practices.html">
-              <span class="en">Security and Design</span></a>
-      </li>
-      <li><a href="/google/play/billing/billing_testing.html">
-              <span class="en">Testing In-app Billing</span></a>
-      </li>
-      <li><a href="/google/play/billing/billing_admin.html">
-              <span class="en">Administering In-app Billing</span></a>
-      </li>
-      <li><a href="/google/play/billing/gp-purchase-status-api.html">
-              <span class="en">Purchase Status API</span></a>
-      </li>
-      <li><a href="/google/play/billing/versions.html">
-              <span class="en">Version Notes</span></a>
-      </li>
-    </ul>
-  </li>
-
-
-
-  <li class="nav-section">
-      <div class="nav-section-header"><a href="/google/gcm/index.html">
-        <span class="en">Google Cloud Messaging</span></a>
-      </div>
-      <ul>
-        <li><a href="/google/gcm/gs.html">
-            <span class="en">Getting Started</span></a>
-        </li>
-        <li><a href="/google/gcm/gcm.html">
-            <span class="en">Architectural Overview</span></a>
-        </li>
-         <li><a href="/google/gcm/ccs.html">
-              <span class="en">Cloud Connection Server</span></a>
-        </li>
-        <li><a href="/google/gcm/notifications.html">
-              <span class="en">User Notifications</span></a>
-        </li>
-        <li><a href="/google/gcm/client.html">
-            <span class="en">GCM Client</span></a>
-        </li>
-        <li><a href="/google/gcm/server.html">
-            <span class="en">GCM Server</span></a>
-        </li>
-        <li><a href="/google/gcm/adv.html">
-            <span class="en">Advanced Topics</span></a>
-        </li>
-        <li><a href="/google/gcm/c2dm.html">
-            <span class="en">Migration</span></a>
-        </li>
-        <li id="gcm-tree-list" class="nav-section">
-          <div class="nav-section-header">
-            <a href="/reference/gcm-packages.html">
-              <span class="en">Reference</span>
-            </a>
-          <div>
-        </li>
-      </ul>
-  </li>
-
-  <li class="nav-section">
-    <div class="nav-section-header"><a href="/google/play/dist.html">
-      <span class="en">Google Play Distribution</span></a>
-    </div>
-    <ul>
-      <li><a href="/google/play/filters.html">
-          <span class="en">Filters on Google Play</span></a>
-      </li>
-
-      <li><a href="/google/play/publishing/multiple-apks.html">
-          <span class="en">Multiple APK Support</span></a>
-      </li>
-      <li><a href="/google/play/expansion-files.html">
-          <span class="en">APK Expansion Files</span></a>
-      </li>
-      <li class="nav-section">
-        <div class="nav-section-header"><a href="/google/play/licensing/index.html">
-          <span class="en">Application Licensing</span></a>
-        </div>
-        <ul>
-          <li><a href="/google/play/licensing/overview.html">
-              <span class="en">Licensing Overview</span></a>
-          </li>
-          <li><a href="/google/play/licensing/setting-up.html">
-              <span class="en">Setting Up for Licensing</span></a>
-          </li>
-          <li><a href="/google/play/licensing/adding-licensing.html">
-              <span class="en">Adding Licensing to Your App</span></a>
-          </li>
-          <li><a href="/google/play/licensing/licensing-reference.html">
-              <span class="en">Licensing Reference</span></a>
-          </li>
-        </ul>
-      </li>
-
-  <li class="nav-section">
-    <div class="nav-section-header"><a href="/google/backup/index.html">
-      Android Backup Service</a>
-    </div>
-    <ul>
-      <li><a href="/google/backup/signup.html">
-          Register</a>
-      </li>
-    </ul>
-  </li>
-
-  </ul>
-
-</li>
-
-
-
-</ul>
-
-<script type="text/javascript">
-<!--
-    buildToggleLists();
-    changeNavLang(getLangPref());
-//-->
-</script>
-
-
-        
-
-      </div>
-      <script type="text/javascript">
-       showGoogleRefTree();
-    
-      </script>
-    </div> <!-- end side-nav -->
-    <script>
-      $(document).ready(function() {
-        scrollIntoView("devdoc-nav");
-        });
-    </script>
-
-
-     
-
-
-
-<div class="col-12"  id="doc-col">
-
-<div id="api-info-block">
-
-
-
-  
-   
-  
-  
-  
-  
-
-
-<div class="sum-details-links">
-
-Summary:
-
-
-
-
-
-  <a href="#constants">Constants</a>
-  
-
-
-
-
-
-
-
-
-
-  &#124; <a href="#inhmethods">Inherited Methods</a>
-
-&#124; <a href="#" onclick="return toggleAllClassInherited()" id="toggleAllClassInherited">[Expand All]</a>
-
-</div><!-- end sum-details-links -->
-<div class="api-level">
-  
-  
-  
-
-</div>
-</div><!-- end api-info-block -->
-
-
-<!-- ======== START OF CLASS DATA ======== -->
-
-<div id="jd-header">
-    public
-     
-    final 
-    
-    class
-<h1 itemprop="name">GamesClientSettings</h1>
-
-
-
-  
-    extends Object<br/>
-  
-  
-  
-
-  
-  
-  
-
-
-</div><!-- end header -->
-
-<div id="naMessage"></div>
-
-<div id="jd-content" class="api apilevel-">
-<table class="jd-inheritance-table">
-
-
-    <tr>
-         	
-        <td colspan="2" class="jd-inheritance-class-cell">java.lang.Object</td>
-    </tr>
-    
-
-    <tr>
-        
-            <td class="jd-inheritance-space">&nbsp;&nbsp;&nbsp;&#x21b3;</td>
-         	
-        <td colspan="1" class="jd-inheritance-class-cell">com.google.android.gms.games.GamesClientSettings</td>
-    </tr>
-    
-
-</table>
-
-
-
-
-
-
-
-<div class="jd-descr">
-
-
-<h2>Class Overview</h2>
-<p itemprop="articleBody">Settings for <code><a href="/reference/com/google/android/gms/games/GamesClient.html">GamesClient</a></code>.
-</p>
-
-
-
-
-
-</div><!-- jd-descr -->
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-<div class="jd-descr">
-
-
-<h2>Summary</h2>
-
-
-
-
-
-
-
-
-
-
-
-
-
-<!-- =========== ENUM CONSTANT SUMMARY =========== -->
-<table id="constants" class="jd-sumtable"><tr><th colspan="12">Constants</th></tr>
-
-
-    
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol">int</td>
-        <td class="jd-linkcol"><a href="/reference/com/google/android/gms/games/GamesClientSettings.html#POPUP_POSITION_BOTTOM">POPUP_POSITION_BOTTOM</a></td>
-        <td class="jd-descrcol" width="100%">Display games service popups (achievements, welcome, ...) at the bottom of the screen
-</td>
-    </tr>
-    
-    
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol">int</td>
-        <td class="jd-linkcol"><a href="/reference/com/google/android/gms/games/GamesClientSettings.html#POPUP_POSITION_TOP">POPUP_POSITION_TOP</a></td>
-        <td class="jd-descrcol" width="100%">Display games service popups (achievements, welcome, ...) at the top of the screen
-</td>
-    </tr>
-    
-    
-
-</table>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-<!-- ========== METHOD SUMMARY =========== -->
-<table id="inhmethods" class="jd-sumtable"><tr><th>
-  <a href="#" class="toggle-all" onclick="return toggleAllInherited(this, null)">[Expand]</a>
-  <div style="clear:left;">Inherited Methods</div></th></tr>
-
-
-<tr class="api apilevel-" >
-<td colspan="12">
-  <a href="#" onclick="return toggleInherited(this, null)" id="inherited-methods-java.lang.Object" class="jd-expando-trigger closed"
-          ><img id="inherited-methods-java.lang.Object-trigger"
-          src="/assets/images/triangle-closed.png"
-          class="jd-expando-trigger-img" /></a>
-From class
-
-  java.lang.Object
-
-<div id="inherited-methods-java.lang.Object">
-  <div id="inherited-methods-java.lang.Object-list"
-        class="jd-inheritedlinks">
-  </div>
-  <div id="inherited-methods-java.lang.Object-summary" style="display: none;">
-    <table class="jd-sumtable-expando">
-    
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            Object</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">clone</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            boolean</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">equals</span>(Object arg0)</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">finalize</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            Class&lt;?&gt;</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">getClass</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            int</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">hashCode</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">notify</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">notifyAll</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            
-            
-            
-            String</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">toString</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">wait</span>()</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class=" api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">wait</span>(long arg0, int arg1)</nobr>
-        
-  </td></tr>
-
-
-	 
-    <tr class="alt-color api apilevel-" >
-        <td class="jd-typecol"><nobr>
-            
-            
-            final
-            
-            
-            void</nobr>
-        </td>
-        <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad">wait</span>(long arg0)</nobr>
-        
-  </td></tr>
-
-
-</table>
-  </div>
-</div>
-</td></tr>
-
-
-</table>
-
-
-</div><!-- jd-descr (summary) -->
-
-<!-- Details -->
-
-
-
-
-
-
-
-
-<!-- XML Attributes -->
-
-
-<!-- Enum Values -->
-
-
-<!-- Constants -->
-
-
-<!-- ========= ENUM CONSTANTS DETAIL ======== -->
-<h2>Constants</h2>
-
-
-
-
-<A NAME="POPUP_POSITION_BOTTOM"></A>
-
-<div class="jd-details api apilevel-"> 
-    <h4 class="jd-details-title">
-      <span class="normal">
-        public 
-        static 
-        final 
-        int
-      </span>
-        POPUP_POSITION_BOTTOM
-    </h4>
-      <div class="api-level">
-        
-        
-  
-
-      </div>
-    <div class="jd-details-descr">
-      
-  <div class="jd-tagdata jd-tagdescr"><p>Display games service popups (achievements, welcome, ...) at the bottom of the screen
-</p></div>
-
-    
-        <div class="jd-tagdata">
-        <span class="jd-tagtitle">Constant Value: </span>
-        <span>
-            
-                2
-                (0x00000002)
-            
-        </span>
-        </div>
-    
-    </div>
-</div>
-
-
-
-<A NAME="POPUP_POSITION_TOP"></A>
-
-<div class="jd-details api apilevel-"> 
-    <h4 class="jd-details-title">
-      <span class="normal">
-        public 
-        static 
-        final 
-        int
-      </span>
-        POPUP_POSITION_TOP
-    </h4>
-      <div class="api-level">
-        
-        
-  
-
-      </div>
-    <div class="jd-details-descr">
-      
-  <div class="jd-tagdata jd-tagdescr"><p>Display games service popups (achievements, welcome, ...) at the top of the screen
-</p></div>
-
-    
-        <div class="jd-tagdata">
-        <span class="jd-tagtitle">Constant Value: </span>
-        <span>
-            
-                1
-                (0x00000001)
-            
-        </span>
-        </div>
-    
-    </div>
-</div>
-
-
-
-
-<!-- Fields -->
-
-
-<!-- Public ctors -->
-
-
-
-<!-- ========= CONSTRUCTOR DETAIL ======== -->
-<!-- Protected ctors -->
-
-
-
-<!-- ========= METHOD DETAIL ======== -->
-<!-- Public methdos -->
-
-
-
-<!-- ========= METHOD DETAIL ======== -->
-
-
-
-<!-- ========= END OF CLASS DATA ========= -->
-<A NAME="navbar_top"></A>
-
-<div id="footer" class="wrap" >
-        
-
-  <div id="copyright">
-    
-  Except as noted, this content is licensed under <a
-  href="http://www.apache.org/licenses/LICENSE-2.0">Apache 2.0</a>. 
-  For details and restrictions, see the <a href="/license.html">
-  Content License</a>.
-  </div>
-  <div id="build_info">
-    
-<script src="/timestamp.js" type="text/javascript"></script>
-<script>document.write(BUILD_TIMESTAMP)</script>
-
-  </div>
-
-
-  <div id="footerlinks">
-    
-  <p>
-    <a href="/about/index.html">About Android</a>&nbsp;&nbsp;|&nbsp;
-    <a href="/legal.html">Legal</a>&nbsp;&nbsp;|&nbsp;
-    <a href="/support.html">Support</a>
-  </p>
-  </div>
-
-</div> <!-- end footer -->
-</div> <!-- jd-content -->
-
-</div><!-- end doc-content -->
-
-</div> <!-- end body-content --> 
-
-
-
-
-
-
-</body>
-</html>
diff --git a/docs/html/reference/com/google/android/gms/games/PlayerBuffer.html b/docs/html/reference/com/google/android/gms/games/PlayerBuffer.html
index e0ad326..23425bc 100644
--- a/docs/html/reference/com/google/android/gms/games/PlayerBuffer.html
+++ b/docs/html/reference/com/google/android/gms/games/PlayerBuffer.html
@@ -1278,13 +1278,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the item at the specified position. Note that the objects returned from subsequent
  invocations of this method for the same position may not be identical objects, but will be
  equal in value. In other words:
-
- <pre>
- <code>
- buffer.get(i) == buffer.get(i) may return false.
- buffer.get(i).equals(buffer.get(i)) will return true.
- </code>
- </pre></p></div>
+ <p>
+ <code>buffer.get(i) == buffer.get(i)</code> may return false.
+ <p>
+ <code>buffer.get(i).equals(buffer.get(i))</code> will return true.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>position</td>
+          <td>The position of the item to retrieve.</td>
+        </tr>
+      </table>
+  </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>the item at <code>position</code> in this buffer.
diff --git a/docs/html/reference/com/google/android/gms/games/RealTimeSocket.html b/docs/html/reference/com/google/android/gms/games/RealTimeSocket.html
index 7298e87..1e42cdd 100644
--- a/docs/html/reference/com/google/android/gms/games/RealTimeSocket.html
+++ b/docs/html/reference/com/google/android/gms/games/RealTimeSocket.html
@@ -685,7 +685,7 @@
  <p>
  Use <code><a href="/reference/com/google/android/gms/games/RealTimeSocket.html#getParcelFileDescriptor()">getParcelFileDescriptor()</a></code> to get a file descriptor for read/write, or
  use <code><a href="/reference/com/google/android/gms/games/RealTimeSocket.html#getInputStream()">getInputStream()</a></code> / <code><a href="/reference/com/google/android/gms/games/RealTimeSocket.html#getOutputStream()">getOutputStream()</a></code> to get
- access to an instance of <code><a href="/reference/java/io/InputStream.html">InputStream</a></code> and <code><a href="/reference/java/io/OutputStream.html">OutputStream</a></code>
+ access to an instance of <code><a href="/reference/java/io/InputStream.html">InputStream</a></code> or <code><a href="/reference/java/io/OutputStream.html">OutputStream</a></code>
  respectively.
  <p>
  Calling close() on any of (i) the returned <code><a href="/reference/android/os/ParcelFileDescriptor.html">ParcelFileDescriptor</a></code> or, (ii) the
@@ -957,7 +957,7 @@
  Calling close() on the InputStream will close the socket.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>instance of <code><a href="/reference/java/io/InputStream.html">InputStream</a></code>.</li></ul>
+      <ul class="nolist"><li>An instance of <code><a href="/reference/java/io/InputStream.html">InputStream</a></code>.</li></ul>
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Throws</h5>
@@ -1006,7 +1006,7 @@
  packet will be dropped and an error message will be sent to the log.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>instance of <code><a href="/reference/java/io/OutputStream.html">OutputStream</a></code>.</li></ul>
+      <ul class="nolist"><li>An instance of <code><a href="/reference/java/io/OutputStream.html">OutputStream</a></code>.</li></ul>
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Throws</h5>
@@ -1050,8 +1050,8 @@
  Calling close() on the returned ParcelFileDescriptor will close the socket.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>instance of <code><a href="/reference/android/os/ParcelFileDescriptor.html">ParcelFileDescriptor</a></code> or null if the underlying socket
-         is closed.</li></ul>
+      <ul class="nolist"><li>An instance of <code><a href="/reference/android/os/ParcelFileDescriptor.html">ParcelFileDescriptor</a></code> or null if the underlying
+         socket is closed.</li></ul>
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Throws</h5>
diff --git a/docs/html/reference/com/google/android/gms/games/achievement/Achievement.html b/docs/html/reference/com/google/android/gms/games/achievement/Achievement.html
index cf6e4f9..c40d4df 100644
--- a/docs/html/reference/com/google/android/gms/games/achievement/Achievement.html
+++ b/docs/html/reference/com/google/android/gms/games/achievement/Achievement.html
@@ -1782,7 +1782,7 @@
  this directly. Instead, cache the result of <code><a href="/reference/com/google/android/gms/common/data/Freezable.html#freeze()">freeze()</a></code>.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>player associated with this achievement.
+      <ul class="nolist"><li>The player associated with this achievement.
 </li></ul>
   </div>
 
diff --git a/docs/html/reference/com/google/android/gms/games/achievement/AchievementBuffer.html b/docs/html/reference/com/google/android/gms/games/achievement/AchievementBuffer.html
index 646f741..06bd27a 100644
--- a/docs/html/reference/com/google/android/gms/games/achievement/AchievementBuffer.html
+++ b/docs/html/reference/com/google/android/gms/games/achievement/AchievementBuffer.html
@@ -1278,13 +1278,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the item at the specified position. Note that the objects returned from subsequent
  invocations of this method for the same position may not be identical objects, but will be
  equal in value. In other words:
-
- <pre>
- <code>
- buffer.get(i) == buffer.get(i) may return false.
- buffer.get(i).equals(buffer.get(i)) will return true.
- </code>
- </pre></p></div>
+ <p>
+ <code>buffer.get(i) == buffer.get(i)</code> may return false.
+ <p>
+ <code>buffer.get(i).equals(buffer.get(i))</code> will return true.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>position</td>
+          <td>The position of the item to retrieve.</td>
+        </tr>
+      </table>
+  </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>the item at <code>position</code> in this buffer.
diff --git a/docs/html/reference/com/google/android/gms/games/achievement/OnAchievementUpdatedListener.html b/docs/html/reference/com/google/android/gms/games/achievement/OnAchievementUpdatedListener.html
index 6c859440..d104974 100644
--- a/docs/html/reference/com/google/android/gms/games/achievement/OnAchievementUpdatedListener.html
+++ b/docs/html/reference/com/google/android/gms/games/achievement/OnAchievementUpdatedListener.html
@@ -868,7 +868,7 @@
         </tr>
         <tr>
           <th>achievementId</td>
-          <td>id of the achievement that was updated.
+          <td>The ID of the achievement that was updated.
 </td>
         </tr>
       </table>
diff --git a/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardBuffer.html b/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardBuffer.html
index 0f3911b..2186179 100644
--- a/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardBuffer.html
+++ b/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardBuffer.html
@@ -1343,13 +1343,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the item at the specified position. Note that the objects returned from subsequent
  invocations of this method for the same position may not be identical objects, but will be
  equal in value. In other words:
-
- <pre>
- <code>
- buffer.get(i) == buffer.get(i) may return false.
- buffer.get(i).equals(buffer.get(i)) will return true.
- </code>
- </pre></p></div>
+ <p>
+ <code>buffer.get(i) == buffer.get(i)</code> may return false.
+ <p>
+ <code>buffer.get(i).equals(buffer.get(i))</code> will return true.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>position</td>
+          <td>The position of the item to retrieve.</td>
+        </tr>
+      </table>
+  </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>the item at <code>position</code> in this buffer.
diff --git a/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardScore.html b/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardScore.html
index b0369b3..93d96d3 100644
--- a/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardScore.html
+++ b/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardScore.html
@@ -1338,7 +1338,7 @@
  this directly. Instead, cache the result of <code><a href="/reference/com/google/android/gms/common/data/Freezable.html#freeze()">freeze()</a></code>.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>player associated with this leaderboard score.
+      <ul class="nolist"><li>The player associated with this leaderboard score.
 </li></ul>
   </div>
 
diff --git a/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardScoreBuffer.html b/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardScoreBuffer.html
index 07e266e..2ee0f33 100644
--- a/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardScoreBuffer.html
+++ b/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardScoreBuffer.html
@@ -1278,13 +1278,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the item at the specified position. Note that the objects returned from subsequent
  invocations of this method for the same position may not be identical objects, but will be
  equal in value. In other words:
-
- <pre>
- <code>
- buffer.get(i) == buffer.get(i) may return false.
- buffer.get(i).equals(buffer.get(i)) will return true.
- </code>
- </pre></p></div>
+ <p>
+ <code>buffer.get(i) == buffer.get(i)</code> may return false.
+ <p>
+ <code>buffer.get(i).equals(buffer.get(i))</code> will return true.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>position</td>
+          <td>The position of the item to retrieve.</td>
+        </tr>
+      </table>
+  </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>the item at <code>position</code> in this buffer.
diff --git a/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html b/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html
index 957bcf7..f67626d 100644
--- a/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html
+++ b/docs/html/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html
@@ -1427,9 +1427,8 @@
  is only accurate if <code><a href="/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html#hasPlayerInfo()">hasPlayerInfo()</a></code> returns true.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the String representation of the viewing player's rank, or {@code null)
+      <ul class="nolist"><li>The String representation of the viewing player's rank, or {@code null)
          if the player has no rank for this variant.
-
 </li></ul>
   </div>
 
@@ -1464,7 +1463,7 @@
  accurate if <code><a href="/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html#hasPlayerInfo()">hasPlayerInfo()</a></code> returns true.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the String representation of the viewing player's score, or <code>null</code> if the
+      <ul class="nolist"><li>The String representation of the viewing player's score, or <code>null</code> if the
          player has no score for this variant.
 </li></ul>
   </div>
@@ -1501,7 +1500,7 @@
  this method will return <code><a href="/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html#NUM_SCORES_UNKNOWN">NUM_SCORES_UNKNOWN</a></code>.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the number of scores for this variant, or <code><a href="/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html#NUM_SCORES_UNKNOWN">NUM_SCORES_UNKNOWN</a></code>.
+      <ul class="nolist"><li>The number of scores for this variant, or <code><a href="/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html#NUM_SCORES_UNKNOWN">NUM_SCORES_UNKNOWN</a></code>.
 </li></ul>
   </div>
 
@@ -1536,7 +1535,7 @@
  accurate if <code><a href="/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html#hasPlayerInfo()">hasPlayerInfo()</a></code> returns true.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the long representation of the viewing player's rank, or <code><a href="/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html#PLAYER_RANK_UNKNOWN">PLAYER_RANK_UNKNOWN</a></code>
+      <ul class="nolist"><li>The long representation of the viewing player's rank, or <code><a href="/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html#PLAYER_RANK_UNKNOWN">PLAYER_RANK_UNKNOWN</a></code>
          if the player has no rank for this variant.
 </li></ul>
   </div>
@@ -1572,7 +1571,7 @@
  accurate if <code><a href="/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html#hasPlayerInfo()">hasPlayerInfo()</a></code> returns true.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the long representation of the viewing player's score, or
+      <ul class="nolist"><li>The long representation of the viewing player's score, or
          <code><a href="/reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html#PLAYER_SCORE_UNKNOWN">PLAYER_SCORE_UNKNOWN</a></code> if the player has no score for this variant.
 </li></ul>
   </div>
@@ -1649,7 +1648,7 @@
  a rank.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>whether or not this variant contains score information for the viewing player.
+      <ul class="nolist"><li>Whether or not this variant contains score information for the viewing player.
 </li></ul>
   </div>
 
diff --git a/docs/html/reference/com/google/android/gms/games/leaderboard/OnScoreSubmittedListener.html b/docs/html/reference/com/google/android/gms/games/leaderboard/OnScoreSubmittedListener.html
index 07ee745..42b4e40 100644
--- a/docs/html/reference/com/google/android/gms/games/leaderboard/OnScoreSubmittedListener.html
+++ b/docs/html/reference/com/google/android/gms/games/leaderboard/OnScoreSubmittedListener.html
@@ -857,7 +857,8 @@
         <tr>
           <th>result</td>
           <td>Detailed results of the operation, which includes data regarding whether this
-            was the new high score for any of the supported time spans.
+            was the new high score for any of the supported time spans. Note that the results
+            will only be populated if <code>statusCodes</code> is <code><a href="/reference/com/google/android/gms/games/GamesClient.html#STATUS_OK">STATUS_OK</a></code>.
 </td>
         </tr>
       </table>
diff --git a/docs/html/reference/com/google/android/gms/games/leaderboard/SubmitScoreResult.html b/docs/html/reference/com/google/android/gms/games/leaderboard/SubmitScoreResult.html
index 167b6a6..e33b848 100644
--- a/docs/html/reference/com/google/android/gms/games/leaderboard/SubmitScoreResult.html
+++ b/docs/html/reference/com/google/android/gms/games/leaderboard/SubmitScoreResult.html
@@ -1310,7 +1310,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Retrieves the ID of the leaderboard the score was submitted to.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the ID of the leaderboard.
+      <ul class="nolist"><li>The ID of the leaderboard.
 </li></ul>
   </div>
 
@@ -1344,7 +1344,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Retrieves the ID of the player the score was submitted for.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the ID of submitting player.
+      <ul class="nolist"><li>The ID of submitting player.
 </li></ul>
   </div>
 
@@ -1390,7 +1390,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the appropriate <code><a href="/reference/com/google/android/gms/games/leaderboard/SubmitScoreResult.Result.html">SubmitScoreResult.Result</a></code> or <code>null</code> if no result was returned for the
+      <ul class="nolist"><li>The appropriate <code><a href="/reference/com/google/android/gms/games/leaderboard/SubmitScoreResult.Result.html">SubmitScoreResult.Result</a></code> or <code>null</code> if no result was returned for the
          given time span.
 </li></ul>
   </div>
@@ -1433,7 +1433,7 @@
  </ul></p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the status code of the submit score operation.
+      <ul class="nolist"><li>The status code of the submit score operation.
 </li></ul>
   </div>
 
diff --git a/docs/html/reference/com/google/android/gms/games/multiplayer/Invitation.html b/docs/html/reference/com/google/android/gms/games/multiplayer/Invitation.html
index c9b914f..95eac22 100644
--- a/docs/html/reference/com/google/android/gms/games/multiplayer/Invitation.html
+++ b/docs/html/reference/com/google/android/gms/games/multiplayer/Invitation.html
@@ -1177,7 +1177,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Retrieve the server timestamp at which this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code> was created.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the server timestamp at which this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code> was created.
+      <ul class="nolist"><li>The server timestamp at which this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code> was created.
 </li></ul>
   </div>
 
@@ -1245,7 +1245,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Retrieve the ID of this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the ID of this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.
+      <ul class="nolist"><li>The ID of this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.
 </li></ul>
   </div>
 
@@ -1279,7 +1279,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Retrieve the <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code> who created this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code> who created this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.
+      <ul class="nolist"><li>The <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code> who created this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.
 </li></ul>
   </div>
 
diff --git a/docs/html/reference/com/google/android/gms/games/multiplayer/InvitationBuffer.html b/docs/html/reference/com/google/android/gms/games/multiplayer/InvitationBuffer.html
index 4edeb83..6f906a6 100644
--- a/docs/html/reference/com/google/android/gms/games/multiplayer/InvitationBuffer.html
+++ b/docs/html/reference/com/google/android/gms/games/multiplayer/InvitationBuffer.html
@@ -1343,13 +1343,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the item at the specified position. Note that the objects returned from subsequent
  invocations of this method for the same position may not be identical objects, but will be
  equal in value. In other words:
-
- <pre>
- <code>
- buffer.get(i) == buffer.get(i) may return false.
- buffer.get(i).equals(buffer.get(i)) will return true.
- </code>
- </pre></p></div>
+ <p>
+ <code>buffer.get(i) == buffer.get(i)</code> may return false.
+ <p>
+ <code>buffer.get(i).equals(buffer.get(i))</code> will return true.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>position</td>
+          <td>The position of the item to retrieve.</td>
+        </tr>
+      </table>
+  </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>the item at <code>position</code> in this buffer.
diff --git a/docs/html/reference/com/google/android/gms/games/multiplayer/InvitationEntity.html b/docs/html/reference/com/google/android/gms/games/multiplayer/InvitationEntity.html
index a38ddc4..9615ad3f 100644
--- a/docs/html/reference/com/google/android/gms/games/multiplayer/InvitationEntity.html
+++ b/docs/html/reference/com/google/android/gms/games/multiplayer/InvitationEntity.html
@@ -1772,7 +1772,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Retrieve the server timestamp at which this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code> was created.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the server timestamp at which this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code> was created.
+      <ul class="nolist"><li>The server timestamp at which this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code> was created.
 </li></ul>
   </div>
 
@@ -1840,7 +1840,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Retrieve the ID of this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the ID of this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.
+      <ul class="nolist"><li>The ID of this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.
 </li></ul>
   </div>
 
@@ -1903,7 +1903,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Retrieve the <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code> who created this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code> who created this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.
+      <ul class="nolist"><li>The <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code> who created this <code><a href="/reference/com/google/android/gms/games/multiplayer/Invitation.html">Invitation</a></code>.
 </li></ul>
   </div>
 
@@ -1938,7 +1938,7 @@
  applicable to the given object.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>a list of <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code>s for this object.
+      <ul class="nolist"><li>A list of <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code>s for this object.
 </li></ul>
   </div>
 
diff --git a/docs/html/reference/com/google/android/gms/games/multiplayer/ParticipantBuffer.html b/docs/html/reference/com/google/android/gms/games/multiplayer/ParticipantBuffer.html
index 5a3a803..59e0f54 100644
--- a/docs/html/reference/com/google/android/gms/games/multiplayer/ParticipantBuffer.html
+++ b/docs/html/reference/com/google/android/gms/games/multiplayer/ParticipantBuffer.html
@@ -1278,13 +1278,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the item at the specified position. Note that the objects returned from subsequent
  invocations of this method for the same position may not be identical objects, but will be
  equal in value. In other words:
-
- <pre>
- <code>
- buffer.get(i) == buffer.get(i) may return false.
- buffer.get(i).equals(buffer.get(i)) will return true.
- </code>
- </pre></p></div>
+ <p>
+ <code>buffer.get(i) == buffer.get(i)</code> may return false.
+ <p>
+ <code>buffer.get(i).equals(buffer.get(i))</code> will return true.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>position</td>
+          <td>The position of the item to retrieve.</td>
+        </tr>
+      </table>
+  </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>the item at <code>position</code> in this buffer.
diff --git a/docs/html/reference/com/google/android/gms/games/multiplayer/Participatable.html b/docs/html/reference/com/google/android/gms/games/multiplayer/Participatable.html
index 3b0250d..d543bef 100644
--- a/docs/html/reference/com/google/android/gms/games/multiplayer/Participatable.html
+++ b/docs/html/reference/com/google/android/gms/games/multiplayer/Participatable.html
@@ -901,7 +901,7 @@
  applicable to the given object.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>a list of <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code>s for this object.
+      <ul class="nolist"><li>A list of <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code>s for this object.
 </li></ul>
   </div>
 
diff --git a/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/Room.html b/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/Room.html
index 64c6398..1b12a08 100644
--- a/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/Room.html
+++ b/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/Room.html
@@ -1547,7 +1547,7 @@
  room has no automatch properties.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>bundle containing the automatch criteria for this room.
+      <ul class="nolist"><li>A bundle containing the automatch criteria for this room.
 </li></ul>
   </div>
 
@@ -1928,8 +1928,8 @@
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>Variant specified for this room, if any. A variant is an optional
-         developer-controlled parameter describing the type of game to play, ranging from
-         1-1023 (inclusive). If this room had no variant specified, returns
+         developer-controlled parameter describing the type of game to play. If specified,
+         this value will be a positive integer. If this room had no variant specified, returns
          <code><a href="/reference/com/google/android/gms/games/multiplayer/realtime/Room.html#ROOM_VARIANT_ANY">ROOM_VARIANT_ANY</a></code>.
 </li></ul>
   </div>
diff --git a/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.Builder.html b/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.Builder.html
index 373578b..df6fb49 100644
--- a/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.Builder.html
+++ b/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.Builder.html
@@ -1398,7 +1398,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>invitationId</td>
-          <td>the ID of the invitation to accept.
+          <td>The ID of the invitation to accept.
 </td>
         </tr>
       </table>
@@ -1440,7 +1440,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the message received listener that is called to notify the client when it
+          <td>The message received listener that is called to notify the client when it
             receives a message in a room. The listener is called on the main thread.
 </td>
         </tr>
@@ -1480,7 +1480,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>listener</td>
-          <td>the listener that is called to notify the client when the status of the
+          <td>The listener that is called to notify the client when the status of the
             room has changed. The listener is called on the main thread.
 </td>
         </tr>
@@ -1566,8 +1566,8 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Sets the variant for the room when calling <code><a href="/reference/com/google/android/gms/games/GamesClient.html#createRoom(com.google.android.gms.games.multiplayer.realtime.RoomConfig)">createRoom(RoomConfig)</a></code>. This is an
- optional, developer-controlled parameter describing the type of game to play, and is
- used for auto-matching criteria. Must be either a value from 1 to 1023 (inclusive), or
+ optional, developer-controlled parameter describing the type of game to play, and is used
+ for auto-matching criteria. Must be either a positive integer or
  <code><a href="/reference/com/google/android/gms/games/multiplayer/realtime/Room.html#ROOM_VARIANT_ANY">ROOM_VARIANT_ANY</a></code> (the default) if not desired.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Parameters</h5>
diff --git a/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.html b/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.html
index 93da6bbf..926eeda 100644
--- a/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.html
+++ b/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.html
@@ -1317,15 +1317,15 @@
       <table class="jd-tagtable">
         <tr>
           <th>minAutoMatchPlayers</td>
-          <td>min number of auto-matched players.</td>
+          <td>Minimum number of auto-matched players.</td>
         </tr>
         <tr>
           <th>maxAutoMatchPlayers</td>
-          <td>max number of auto-matched players.</td>
+          <td>Maximum number of auto-matched players.</td>
         </tr>
         <tr>
           <th>exclusiveBitMask</td>
-          <td>exclusive bitmasks for the automatching request. The logical AND of
+          <td>Exclusive bitmasks for the automatching request. The logical AND of
             each pairing of automatching requests must equal zero for auto-match. If there
             are no exclusivity requirements for the game, this value should just be set to 0.</td>
         </tr>
@@ -1333,7 +1333,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>bundle of auto-match criteria data.
+      <ul class="nolist"><li>A bundle of auto-match criteria data.
 </li></ul>
   </div>
 
@@ -1470,7 +1470,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Retrieves the listener for message received from a peer.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>the <code><a href="/reference/com/google/android/gms/games/multiplayer/realtime/RealTimeMessageReceivedListener.html">RealTimeMessageReceivedListener</a></code> that is called when the client has
+      <ul class="nolist"><li>The <code><a href="/reference/com/google/android/gms/games/multiplayer/realtime/RealTimeMessageReceivedListener.html">RealTimeMessageReceivedListener</a></code> that is called when the client has
             received a message from a peer.
 </li></ul>
   </div>
@@ -1571,8 +1571,7 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Retrieves the (optional) developer-controlled parameter describing the type of game to play.
- Must be either a value from 1 to 1023 (inclusive), or <code><a href="/reference/com/google/android/gms/games/multiplayer/realtime/Room.html#ROOM_VARIANT_ANY">ROOM_VARIANT_ANY</a></code> if not
- desired.</p></div>
+ Must be either a positive integer or <code><a href="/reference/com/google/android/gms/games/multiplayer/realtime/Room.html#ROOM_VARIANT_ANY">ROOM_VARIANT_ANY</a></code> if not desired.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>The developer-specified game variant.
diff --git a/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomEntity.html b/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomEntity.html
index e3772e2..7e6861b 100644
--- a/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomEntity.html
+++ b/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomEntity.html
@@ -2044,7 +2044,7 @@
  room has no automatch properties.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>bundle containing the automatch criteria for this room.
+      <ul class="nolist"><li>A bundle containing the automatch criteria for this room.
 </li></ul>
   </div>
 
@@ -2324,7 +2324,7 @@
  applicable to the given object.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>a list of <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code>s for this object.
+      <ul class="nolist"><li>A list of <code><a href="/reference/com/google/android/gms/games/multiplayer/Participant.html">Participant</a></code>s for this object.
 </li></ul>
   </div>
 
diff --git a/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomStatusUpdateListener.html b/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomStatusUpdateListener.html
index 45fd518..988cc0c 100644
--- a/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomStatusUpdateListener.html
+++ b/docs/html/reference/com/google/android/gms/games/multiplayer/realtime/RoomStatusUpdateListener.html
@@ -1059,7 +1059,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>participantIds</td>
-          <td>ID of the peers invited to a room.
+          <td>IDs of the peers invited to a room.
 </td>
         </tr>
       </table>
@@ -1098,7 +1098,7 @@
       <table class="jd-tagtable">
         <tr>
           <th>participantIds</td>
-          <td>ID of the peers invited to a room.
+          <td>IDs of the peers invited to a room.
 </td>
         </tr>
       </table>
diff --git a/docs/html/reference/com/google/android/gms/games/package-summary.html b/docs/html/reference/com/google/android/gms/games/package-summary.html
index 098524a..e5b82b2 100644
--- a/docs/html/reference/com/google/android/gms/games/package-summary.html
+++ b/docs/html/reference/com/google/android/gms/games/package-summary.html
@@ -711,18 +711,14 @@
               <td class="jd-descrcol" width="100%">Builder class for GamesClient.&nbsp;</td>
           </tr>
         <tr class=" api apilevel-" >
-              <td class="jd-linkcol"><a href="/reference/com/google/android/gms/games/GamesClientSettings.html">GamesClientSettings</a></td>
-              <td class="jd-descrcol" width="100%">Settings for <code><a href="/reference/com/google/android/gms/games/GamesClient.html">GamesClient</a></code>.&nbsp;</td>
-          </tr>
-        <tr class="alt-color api apilevel-" >
               <td class="jd-linkcol"><a href="/reference/com/google/android/gms/games/PageDirection.html">PageDirection</a></td>
               <td class="jd-descrcol" width="100%">Direction constants for pagination over data sets.&nbsp;</td>
           </tr>
-        <tr class=" api apilevel-" >
+        <tr class="alt-color api apilevel-" >
               <td class="jd-linkcol"><a href="/reference/com/google/android/gms/games/PlayerBuffer.html">PlayerBuffer</a></td>
               <td class="jd-descrcol" width="100%">Data structure providing access to a list of players.&nbsp;</td>
           </tr>
-        <tr class="alt-color api apilevel-" >
+        <tr class=" api apilevel-" >
               <td class="jd-linkcol"><a href="/reference/com/google/android/gms/games/PlayerEntity.html">PlayerEntity</a></td>
               <td class="jd-descrcol" width="100%">Data object representing a set of Player data.&nbsp;</td>
           </tr>
diff --git a/docs/html/reference/com/google/android/gms/location/ActivityRecognitionClient.html b/docs/html/reference/com/google/android/gms/location/ActivityRecognitionClient.html
index 492532e..5b50466 100644
--- a/docs/html/reference/com/google/android/gms/location/ActivityRecognitionClient.html
+++ b/docs/html/reference/com/google/android/gms/location/ActivityRecognitionClient.html
@@ -756,7 +756,7 @@
  ActivityRecognitionClient mActivityRecognitionClient =
          new ActivityRecognitionClient(this, this, this);
  mActivityRecognitionClient.connect();
- 
+
   // Called when a connection to the ActivityRecognitionService has been established.
  public void onConnected(Bundle connectionHint) {
      Intent intent = new Intent(this, MyIntentService.class);
@@ -1697,7 +1697,7 @@
  client actions caused by the user with a call to this method.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is connected to the service.
+      <ul class="nolist"><li>true if the client is connected to the service.
 </li></ul>
   </div>
 
@@ -1731,7 +1731,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Checks if the client is attempting to connect to the service.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is attempting to connect to the service.
+      <ul class="nolist"><li>true if the client is attempting to connect to the service.
 </li></ul>
   </div>
 
@@ -1775,7 +1775,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              events.</li></ul>
   </div>
 
@@ -1819,7 +1819,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              failed events.</li></ul>
   </div>
 
@@ -1851,9 +1851,10 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection events from this <code>GooglePlayServicesClient</code>.
- If we are already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code> method
- will be called immediately.  Applications should balance calls to this method with calls to
- <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking resources.
+ If the service is already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code>
+ method will be called immediately.  Applications should balance calls to this method with
+ calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection events, this
  method will not add a duplicate entry for the same listener, but <strong>will</strong>
@@ -1902,11 +1903,12 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection failed events from this
- <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if we are not
- already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code>
- method will not be called immediately.  Applications should balance calls to this method with
- calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid
- leaking resources.
+ <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if the service
+ is not already connected, the listener's
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code> method will not be called immediately.
+ Applications should balance calls to this method with calls to
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection failed events, this
  method will not add a duplicate entry for the same listener.
diff --git a/docs/html/reference/com/google/android/gms/location/LocationClient.html b/docs/html/reference/com/google/android/gms/location/LocationClient.html
index 6652c0a..d7bd08a 100644
--- a/docs/html/reference/com/google/android/gms/location/LocationClient.html
+++ b/docs/html/reference/com/google/android/gms/location/LocationClient.html
@@ -2291,7 +2291,7 @@
  client actions caused by the user with a call to this method.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is connected to the service.
+      <ul class="nolist"><li>true if the client is connected to the service.
 </li></ul>
   </div>
 
@@ -2325,7 +2325,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Checks if the client is attempting to connect to the service.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is attempting to connect to the service.
+      <ul class="nolist"><li>true if the client is attempting to connect to the service.
 </li></ul>
   </div>
 
@@ -2369,7 +2369,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              events.</li></ul>
   </div>
 
@@ -2413,7 +2413,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              failed events.</li></ul>
   </div>
 
@@ -2445,9 +2445,10 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection events from this <code>GooglePlayServicesClient</code>.
- If we are already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code> method
- will be called immediately.  Applications should balance calls to this method with calls to
- <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking resources.
+ If the service is already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code>
+ method will be called immediately.  Applications should balance calls to this method with
+ calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection events, this
  method will not add a duplicate entry for the same listener, but <strong>will</strong>
@@ -2496,11 +2497,12 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection failed events from this
- <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if we are not
- already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code>
- method will not be called immediately.  Applications should balance calls to this method with
- calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid
- leaking resources.
+ <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if the service
+ is not already connected, the listener's
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code> method will not be called immediately.
+ Applications should balance calls to this method with calls to
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection failed events, this
  method will not add a duplicate entry for the same listener.
diff --git a/docs/html/reference/com/google/android/gms/location/LocationRequest.html b/docs/html/reference/com/google/android/gms/location/LocationRequest.html
index 6eaf102..3ce6ef9 100644
--- a/docs/html/reference/com/google/android/gms/location/LocationRequest.html
+++ b/docs/html/reference/com/google/android/gms/location/LocationRequest.html
@@ -789,8 +789,8 @@
  slower interval, and the location object will be obfuscated to only show a coarse level of
  accuracy.
 
- <p>All location requests are considered hints, and you may receive locations that are more
- accurate, less accurate, and slower than requested.
+ <p>All location requests are considered hints, and you may receive locations that are
+ more/less accurate, and faster/slower than requested.
 </p>
 
 
@@ -998,6 +998,22 @@
             
             
             
+            boolean</nobr>
+        </td>
+        <td class="jd-linkcol" width="100%"><nobr>
+        <span class="sympad"><a href="/reference/com/google/android/gms/location/LocationRequest.html#equals(java.lang.Object)">equals</a></span>(Object object)</nobr>
+        
+  </td></tr>
+
+
+	 
+    <tr class=" api apilevel-" >
+        <td class="jd-typecol"><nobr>
+            
+            
+            
+            
+            
             long</nobr>
         </td>
         <td class="jd-linkcol" width="100%"><nobr>
@@ -1009,7 +1025,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1027,7 +1043,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1045,7 +1061,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1063,7 +1079,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1081,7 +1097,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1101,6 +1117,22 @@
 
 
 	 
+    <tr class=" api apilevel-" >
+        <td class="jd-typecol"><nobr>
+            
+            
+            
+            
+            
+            int</nobr>
+        </td>
+        <td class="jd-linkcol" width="100%"><nobr>
+        <span class="sympad"><a href="/reference/com/google/android/gms/location/LocationRequest.html#hashCode()">hashCode</a></span>()</nobr>
+        
+  </td></tr>
+
+
+	 
     <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
@@ -1847,6 +1879,35 @@
 </div>
 
 
+<A NAME="equals(java.lang.Object)"></A>
+
+<div class="jd-details api apilevel-"> 
+    <h4 class="jd-details-title">
+      <span class="normal">
+        public 
+         
+         
+         
+         
+        boolean
+      </span>
+      <span class="sympad">equals</span>
+      <span class="normal">(Object object)</span>
+    </h4>
+      <div class="api-level">
+        <div></div>
+        
+  
+
+      </div>
+    <div class="jd-details-descr">
+      
+  <div class="jd-tagdata jd-tagdescr"><p></p></div>
+
+    </div>
+</div>
+
+
 <A NAME="getExpirationTime()"></A>
 
 <div class="jd-details api apilevel-"> 
@@ -2062,6 +2123,35 @@
 </div>
 
 
+<A NAME="hashCode()"></A>
+
+<div class="jd-details api apilevel-"> 
+    <h4 class="jd-details-title">
+      <span class="normal">
+        public 
+         
+         
+         
+         
+        int
+      </span>
+      <span class="sympad">hashCode</span>
+      <span class="normal">()</span>
+    </h4>
+      <div class="api-level">
+        <div></div>
+        
+  
+
+      </div>
+    <div class="jd-details-descr">
+      
+  <div class="jd-tagdata jd-tagdescr"><p></p></div>
+
+    </div>
+</div>
+
+
 <A NAME="setExpirationDuration(long)"></A>
 
 <div class="jd-details api apilevel-"> 
diff --git a/docs/html/reference/com/google/android/gms/maps/MapFragment.html b/docs/html/reference/com/google/android/gms/maps/MapFragment.html
index 0664856..72f86e5 100644
--- a/docs/html/reference/com/google/android/gms/maps/MapFragment.html
+++ b/docs/html/reference/com/google/android/gms/maps/MapFragment.html
@@ -1045,7 +1045,7 @@
             void</nobr>
         </td>
         <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad"><a href="/reference/com/google/android/gms/maps/MapFragment.html#onAttach(android.app.Activity)">onAttach</a></span>(Activity activity)</nobr>
+        <span class="sympad"><a href="/reference/com/google/android/gms/maps/MapFragment.html#onActivityCreated(android.os.Bundle)">onActivityCreated</a></span>(Bundle savedInstanceState)</nobr>
         
   </td></tr>
 
@@ -1061,13 +1061,29 @@
             void</nobr>
         </td>
         <td class="jd-linkcol" width="100%"><nobr>
+        <span class="sympad"><a href="/reference/com/google/android/gms/maps/MapFragment.html#onAttach(android.app.Activity)">onAttach</a></span>(Activity activity)</nobr>
+        
+  </td></tr>
+
+
+	 
+    <tr class=" api apilevel-" >
+        <td class="jd-typecol"><nobr>
+            
+            
+            
+            
+            
+            void</nobr>
+        </td>
+        <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/com/google/android/gms/maps/MapFragment.html#onCreate(android.os.Bundle)">onCreate</a></span>(Bundle savedInstanceState)</nobr>
         
   </td></tr>
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1083,7 +1099,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1099,7 +1115,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1115,7 +1131,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1133,7 +1149,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1149,7 +1165,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1165,7 +1181,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1181,7 +1197,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1197,7 +1213,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2879,6 +2895,35 @@
 </div>
 
 
+<A NAME="onActivityCreated(android.os.Bundle)"></A>
+
+<div class="jd-details api apilevel-"> 
+    <h4 class="jd-details-title">
+      <span class="normal">
+        public 
+         
+         
+         
+         
+        void
+      </span>
+      <span class="sympad">onActivityCreated</span>
+      <span class="normal">(Bundle savedInstanceState)</span>
+    </h4>
+      <div class="api-level">
+        <div></div>
+        
+  
+
+      </div>
+    <div class="jd-details-descr">
+      
+  <div class="jd-tagdata jd-tagdescr"><p></p></div>
+
+    </div>
+</div>
+
+
 <A NAME="onAttach(android.app.Activity)"></A>
 
 <div class="jd-details api apilevel-"> 
diff --git a/docs/html/reference/com/google/android/gms/maps/SupportMapFragment.html b/docs/html/reference/com/google/android/gms/maps/SupportMapFragment.html
index 9ae0a84..3bccdee 100644
--- a/docs/html/reference/com/google/android/gms/maps/SupportMapFragment.html
+++ b/docs/html/reference/com/google/android/gms/maps/SupportMapFragment.html
@@ -944,7 +944,7 @@
             void</nobr>
         </td>
         <td class="jd-linkcol" width="100%"><nobr>
-        <span class="sympad"><a href="/reference/com/google/android/gms/maps/SupportMapFragment.html#onAttach(android.app.Activity)">onAttach</a></span>(Activity activity)</nobr>
+        <span class="sympad"><a href="/reference/com/google/android/gms/maps/SupportMapFragment.html#onActivityCreated(android.os.Bundle)">onActivityCreated</a></span>(Bundle savedInstanceState)</nobr>
         
   </td></tr>
 
@@ -960,13 +960,29 @@
             void</nobr>
         </td>
         <td class="jd-linkcol" width="100%"><nobr>
+        <span class="sympad"><a href="/reference/com/google/android/gms/maps/SupportMapFragment.html#onAttach(android.app.Activity)">onAttach</a></span>(Activity activity)</nobr>
+        
+  </td></tr>
+
+
+	 
+    <tr class=" api apilevel-" >
+        <td class="jd-typecol"><nobr>
+            
+            
+            
+            
+            
+            void</nobr>
+        </td>
+        <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/com/google/android/gms/maps/SupportMapFragment.html#onCreate(android.os.Bundle)">onCreate</a></span>(Bundle savedInstanceState)</nobr>
         
   </td></tr>
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -982,7 +998,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -998,7 +1014,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1014,7 +1030,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1032,7 +1048,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1048,7 +1064,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1064,7 +1080,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1080,7 +1096,7 @@
 
 
 	 
-    <tr class="alt-color api apilevel-" >
+    <tr class=" api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -1096,7 +1112,7 @@
 
 
 	 
-    <tr class=" api apilevel-" >
+    <tr class="alt-color api apilevel-" >
         <td class="jd-typecol"><nobr>
             
             
@@ -2768,6 +2784,35 @@
 </div>
 
 
+<A NAME="onActivityCreated(android.os.Bundle)"></A>
+
+<div class="jd-details api apilevel-"> 
+    <h4 class="jd-details-title">
+      <span class="normal">
+        public 
+         
+         
+         
+         
+        void
+      </span>
+      <span class="sympad">onActivityCreated</span>
+      <span class="normal">(Bundle savedInstanceState)</span>
+    </h4>
+      <div class="api-level">
+        <div></div>
+        
+  
+
+      </div>
+    <div class="jd-details-descr">
+      
+  <div class="jd-tagdata jd-tagdescr"><p></p></div>
+
+    </div>
+</div>
+
+
 <A NAME="onAttach(android.app.Activity)"></A>
 
 <div class="jd-details api apilevel-"> 
diff --git a/docs/html/reference/com/google/android/gms/panorama/PanoramaClient.html b/docs/html/reference/com/google/android/gms/panorama/PanoramaClient.html
index 832ae26..39a7525 100644
--- a/docs/html/reference/com/google/android/gms/panorama/PanoramaClient.html
+++ b/docs/html/reference/com/google/android/gms/panorama/PanoramaClient.html
@@ -1695,7 +1695,7 @@
  client actions caused by the user with a call to this method.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is connected to the service.
+      <ul class="nolist"><li>true if the client is connected to the service.
 </li></ul>
   </div>
 
@@ -1729,7 +1729,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Checks if the client is attempting to connect to the service.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is attempting to connect to the service.
+      <ul class="nolist"><li>true if the client is attempting to connect to the service.
 </li></ul>
   </div>
 
@@ -1773,7 +1773,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              events.</li></ul>
   </div>
 
@@ -1817,7 +1817,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              failed events.</li></ul>
   </div>
 
@@ -1940,9 +1940,10 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection events from this <code>GooglePlayServicesClient</code>.
- If we are already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code> method
- will be called immediately.  Applications should balance calls to this method with calls to
- <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking resources.
+ If the service is already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code>
+ method will be called immediately.  Applications should balance calls to this method with
+ calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection events, this
  method will not add a duplicate entry for the same listener, but <strong>will</strong>
@@ -1991,11 +1992,12 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection failed events from this
- <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if we are not
- already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code>
- method will not be called immediately.  Applications should balance calls to this method with
- calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid
- leaking resources.
+ <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if the service
+ is not already connected, the listener's
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code> method will not be called immediately.
+ Applications should balance calls to this method with calls to
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection failed events, this
  method will not add a duplicate entry for the same listener.
diff --git a/docs/html/reference/com/google/android/gms/plus/PlusClient.Builder.html b/docs/html/reference/com/google/android/gms/plus/PlusClient.Builder.html
index 5d0383e..bcd39f6 100644
--- a/docs/html/reference/com/google/android/gms/plus/PlusClient.Builder.html
+++ b/docs/html/reference/com/google/android/gms/plus/PlusClient.Builder.html
@@ -825,7 +825,7 @@
         <td class="jd-linkcol" width="100%"><nobr>
         <span class="sympad"><a href="/reference/com/google/android/gms/plus/PlusClient.Builder.html#build()">build</a></span>()</nobr>
         
-        <div class="jd-descrdiv">Creates a new <code><a href="/reference/com/google/android/gms/plus/PlusClient.html">PlusClient</a></code>.</div>
+        <div class="jd-descrdiv">Builds a new <code><a href="/reference/com/google/android/gms/plus/PlusClient.html">PlusClient</a></code> object for communicating with the Google+ APIs.</div>
   
   </td></tr>
 
@@ -1239,8 +1239,12 @@
       </div>
     <div class="jd-details-descr">
       
-  <div class="jd-tagdata jd-tagdescr"><p>Creates a new <code><a href="/reference/com/google/android/gms/plus/PlusClient.html">PlusClient</a></code>.
-</p></div>
+  <div class="jd-tagdata jd-tagdescr"><p>Builds a new <code><a href="/reference/com/google/android/gms/plus/PlusClient.html">PlusClient</a></code> object for communicating with the Google+ APIs.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Returns</h5>
+      <ul class="nolist"><li>The <code><a href="/reference/com/google/android/gms/plus/PlusClient.html">PlusClient</a></code> object.
+</li></ul>
+  </div>
 
     </div>
 </div>
@@ -1390,7 +1394,7 @@
 
  <p>
  See <a href="https://developers.google.com/+/api/moment-types">Types of app
- activity</a>for the full list of valid app activity types. Example usage:
+ activity</a> for the full list of valid app activity types. Example usage:
  <pre>
       plusClientBuilder.setVisibleActivities(
           "http://schemas.google.com/AddActivity",
diff --git a/docs/html/reference/com/google/android/gms/plus/PlusClient.html b/docs/html/reference/com/google/android/gms/plus/PlusClient.html
index 529e2ad..e73301a 100644
--- a/docs/html/reference/com/google/android/gms/plus/PlusClient.html
+++ b/docs/html/reference/com/google/android/gms/plus/PlusClient.html
@@ -2024,7 +2024,7 @@
  client actions caused by the user with a call to this method.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is connected to the service.
+      <ul class="nolist"><li>true if the client is connected to the service.
 </li></ul>
   </div>
 
@@ -2058,7 +2058,7 @@
   <div class="jd-tagdata jd-tagdescr"><p>Checks if the client is attempting to connect to the service.</p></div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the client is attempting to connect to the service.
+      <ul class="nolist"><li>true if the client is attempting to connect to the service.
 </li></ul>
   </div>
 
@@ -2102,7 +2102,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              events.</li></ul>
   </div>
 
@@ -2146,7 +2146,7 @@
   </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
-      <ul class="nolist"><li>true If the specified listener is currently registered to receive connection
+      <ul class="nolist"><li>true if the specified listener is currently registered to receive connection
              failed events.</li></ul>
   </div>
 
@@ -2320,7 +2320,7 @@
         <tr>
           <th>orderBy</td>
           <td>The order to return people in.  Valid values are:<p>
-             <code><a href="/reference/com/google/android/gms/plus/model/people/Person.OrderBy.html#ALPHABETICAL">ALPHABETICAL</a></code> - Order the people by their display name.
+             <code><a href="/reference/com/google/android/gms/plus/model/people/Person.OrderBy.html#ALPHABETICAL">ALPHABETICAL</a></code> - Order the people by their display name.<p>
              <code><a href="/reference/com/google/android/gms/plus/model/people/Person.OrderBy.html#BEST">BEST</a></code> - Order people based on the relevance to the viewer.</td>
         </tr>
         <tr>
@@ -2473,9 +2473,10 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection events from this <code>GooglePlayServicesClient</code>.
- If we are already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code> method
- will be called immediately.  Applications should balance calls to this method with calls to
- <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking resources.
+ If the service is already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html#onConnected(android.os.Bundle)">onConnected(Bundle)</a></code>
+ method will be called immediately.  Applications should balance calls to this method with
+ calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">unregisterConnectionCallbacks(ConnectionCallbacks)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection events, this
  method will not add a duplicate entry for the same listener, but <strong>will</strong>
@@ -2524,11 +2525,12 @@
     <div class="jd-details-descr">
       
   <div class="jd-tagdata jd-tagdescr"><p>Registers a listener to receive connection failed events from this
- <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if we are not
- already connected, the listener's <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code>
- method will not be called immediately.  Applications should balance calls to this method with
- calls to <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid
- leaking resources.
+ <code>GooglePlayServicesClient</code>. Unlike <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#registerConnectionCallbacks(com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks)">registerConnectionCallbacks(GooglePlayServicesClient.ConnectionCallbacks)</a></code>, if the service
+ is not already connected, the listener's
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html#onConnectionFailed(com.google.android.gms.common.ConnectionResult)">onConnectionFailed(ConnectionResult)</a></code> method will not be called immediately.
+ Applications should balance calls to this method with calls to
+ <code><a href="/reference/com/google/android/gms/common/GooglePlayServicesClient.html#unregisterConnectionFailedListener(com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener)">unregisterConnectionFailedListener(OnConnectionFailedListener)</a></code> to avoid leaking
+ resources.
  <p>
  If the specified listener is already registered to receive connection failed events, this
  method will not add a duplicate entry for the same listener.
diff --git a/docs/html/reference/com/google/android/gms/plus/model/moments/MomentBuffer.html b/docs/html/reference/com/google/android/gms/plus/model/moments/MomentBuffer.html
index fac199f..893a3ee 100644
--- a/docs/html/reference/com/google/android/gms/plus/model/moments/MomentBuffer.html
+++ b/docs/html/reference/com/google/android/gms/plus/model/moments/MomentBuffer.html
@@ -1278,13 +1278,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the item at the specified position. Note that the objects returned from subsequent
  invocations of this method for the same position may not be identical objects, but will be
  equal in value. In other words:
-
- <pre>
- <code>
- buffer.get(i) == buffer.get(i) may return false.
- buffer.get(i).equals(buffer.get(i)) will return true.
- </code>
- </pre></p></div>
+ <p>
+ <code>buffer.get(i) == buffer.get(i)</code> may return false.
+ <p>
+ <code>buffer.get(i).equals(buffer.get(i))</code> will return true.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>position</td>
+          <td>The position of the item to retrieve.</td>
+        </tr>
+      </table>
+  </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>the item at <code>position</code> in this buffer.
diff --git a/docs/html/reference/com/google/android/gms/plus/model/people/PersonBuffer.html b/docs/html/reference/com/google/android/gms/plus/model/people/PersonBuffer.html
index b722bcf..b58496b3 100644
--- a/docs/html/reference/com/google/android/gms/plus/model/people/PersonBuffer.html
+++ b/docs/html/reference/com/google/android/gms/plus/model/people/PersonBuffer.html
@@ -1278,13 +1278,19 @@
   <div class="jd-tagdata jd-tagdescr"><p>Get the item at the specified position. Note that the objects returned from subsequent
  invocations of this method for the same position may not be identical objects, but will be
  equal in value. In other words:
-
- <pre>
- <code>
- buffer.get(i) == buffer.get(i) may return false.
- buffer.get(i).equals(buffer.get(i)) will return true.
- </code>
- </pre></p></div>
+ <p>
+ <code>buffer.get(i) == buffer.get(i)</code> may return false.
+ <p>
+ <code>buffer.get(i).equals(buffer.get(i))</code> will return true.</p></div>
+  <div class="jd-tagdata">
+      <h5 class="jd-tagtitle">Parameters</h5>
+      <table class="jd-tagtable">
+        <tr>
+          <th>position</td>
+          <td>The position of the item to retrieve.</td>
+        </tr>
+      </table>
+  </div>
   <div class="jd-tagdata">
       <h5 class="jd-tagtitle">Returns</h5>
       <ul class="nolist"><li>the item at <code>position</code> in this buffer.
diff --git a/docs/html/reference/gms-packages.html b/docs/html/reference/gms-packages.html
index 5a7ae07..ff13938 100644
--- a/docs/html/reference/gms-packages.html
+++ b/docs/html/reference/gms-packages.html
@@ -670,7 +670,7 @@
     <tr class=" api apilevel-" >
         <td class="jd-linkcol">
   <a href="/reference/com/google/android/gms/common/images/package-summary.html">com.google.android.gms.common.images</a></td>
-        <td class="jd-descrcol" width="100%"></td>
+        <td class="jd-descrcol" width="100%">Contains classes for loading images from Google Play services.</td>
     </tr>
 
 
diff --git a/docs/html/reference/gms_lists.js b/docs/html/reference/gms_lists.js
index fe58709..a0e58fb 100644
--- a/docs/html/reference/gms_lists.js
+++ b/docs/html/reference/gms_lists.js
@@ -1,201 +1,199 @@
 var GMS_DATA = [
-      { id:0, label:"com.google.android.gms", link:"reference/com/google/android/gms/package-summary.html", type:"package" },
-      { id:1, label:"com.google.android.gms.R", link:"reference/com/google/android/gms/R.html", type:"class" },
-      { id:2, label:"com.google.android.gms.R.attr", link:"reference/com/google/android/gms/R.attr.html", type:"class" },
-      { id:3, label:"com.google.android.gms.R.color", link:"reference/com/google/android/gms/R.color.html", type:"class" },
-      { id:4, label:"com.google.android.gms.R.drawable", link:"reference/com/google/android/gms/R.drawable.html", type:"class" },
-      { id:5, label:"com.google.android.gms.R.id", link:"reference/com/google/android/gms/R.id.html", type:"class" },
-      { id:6, label:"com.google.android.gms.R.string", link:"reference/com/google/android/gms/R.string.html", type:"class" },
-      { id:7, label:"com.google.android.gms.R.styleable", link:"reference/com/google/android/gms/R.styleable.html", type:"class" },
-      { id:8, label:"com.google.android.gms.appstate", link:"reference/com/google/android/gms/appstate/package-summary.html", type:"package" },
-      { id:9, label:"com.google.android.gms.appstate.AppState", link:"reference/com/google/android/gms/appstate/AppState.html", type:"class" },
-      { id:10, label:"com.google.android.gms.appstate.AppStateBuffer", link:"reference/com/google/android/gms/appstate/AppStateBuffer.html", type:"class" },
-      { id:11, label:"com.google.android.gms.appstate.AppStateClient", link:"reference/com/google/android/gms/appstate/AppStateClient.html", type:"class" },
-      { id:12, label:"com.google.android.gms.appstate.AppStateClient.Builder", link:"reference/com/google/android/gms/appstate/AppStateClient.Builder.html", type:"class" },
-      { id:13, label:"com.google.android.gms.appstate.OnSignOutCompleteListener", link:"reference/com/google/android/gms/appstate/OnSignOutCompleteListener.html", type:"class" },
-      { id:14, label:"com.google.android.gms.appstate.OnStateDeletedListener", link:"reference/com/google/android/gms/appstate/OnStateDeletedListener.html", type:"class" },
-      { id:15, label:"com.google.android.gms.appstate.OnStateListLoadedListener", link:"reference/com/google/android/gms/appstate/OnStateListLoadedListener.html", type:"class" },
-      { id:16, label:"com.google.android.gms.appstate.OnStateLoadedListener", link:"reference/com/google/android/gms/appstate/OnStateLoadedListener.html", type:"class" },
-      { id:17, label:"com.google.android.gms.auth", link:"reference/com/google/android/gms/auth/package-summary.html", type:"package" },
-      { id:18, label:"com.google.android.gms.auth.GoogleAuthException", link:"reference/com/google/android/gms/auth/GoogleAuthException.html", type:"class" },
-      { id:19, label:"com.google.android.gms.auth.GoogleAuthUtil", link:"reference/com/google/android/gms/auth/GoogleAuthUtil.html", type:"class" },
-      { id:20, label:"com.google.android.gms.auth.GooglePlayServicesAvailabilityException", link:"reference/com/google/android/gms/auth/GooglePlayServicesAvailabilityException.html", type:"class" },
-      { id:21, label:"com.google.android.gms.auth.UserRecoverableAuthException", link:"reference/com/google/android/gms/auth/UserRecoverableAuthException.html", type:"class" },
-      { id:22, label:"com.google.android.gms.auth.UserRecoverableNotifiedException", link:"reference/com/google/android/gms/auth/UserRecoverableNotifiedException.html", type:"class" },
-      { id:23, label:"com.google.android.gms.common", link:"reference/com/google/android/gms/common/package-summary.html", type:"package" },
-      { id:24, label:"com.google.android.gms.common.AccountPicker", link:"reference/com/google/android/gms/common/AccountPicker.html", type:"class" },
-      { id:25, label:"com.google.android.gms.common.ConnectionResult", link:"reference/com/google/android/gms/common/ConnectionResult.html", type:"class" },
-      { id:26, label:"com.google.android.gms.common.GooglePlayServicesClient", link:"reference/com/google/android/gms/common/GooglePlayServicesClient.html", type:"class" },
-      { id:27, label:"com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks", link:"reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html", type:"class" },
-      { id:28, label:"com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener", link:"reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html", type:"class" },
-      { id:29, label:"com.google.android.gms.common.GooglePlayServicesNotAvailableException", link:"reference/com/google/android/gms/common/GooglePlayServicesNotAvailableException.html", type:"class" },
-      { id:30, label:"com.google.android.gms.common.GooglePlayServicesUtil", link:"reference/com/google/android/gms/common/GooglePlayServicesUtil.html", type:"class" },
-      { id:31, label:"com.google.android.gms.common.Scopes", link:"reference/com/google/android/gms/common/Scopes.html", type:"class" },
-      { id:32, label:"com.google.android.gms.common.SignInButton", link:"reference/com/google/android/gms/common/SignInButton.html", type:"class" },
-      { id:33, label:"com.google.android.gms.common.data", link:"reference/com/google/android/gms/common/data/package-summary.html", type:"package" },
-      { id:34, label:"com.google.android.gms.common.data.DataBuffer", link:"reference/com/google/android/gms/common/data/DataBuffer.html", type:"class" },
-      { id:35, label:"com.google.android.gms.common.data.DataBufferUtils", link:"reference/com/google/android/gms/common/data/DataBufferUtils.html", type:"class" },
-      { id:36, label:"com.google.android.gms.common.data.Freezable", link:"reference/com/google/android/gms/common/data/Freezable.html", type:"class" },
-      { id:37, label:"com.google.android.gms.common.images", link:"reference/com/google/android/gms/common/images/package-summary.html", type:"package" },
-      { id:38, label:"com.google.android.gms.common.images.ImageManager", link:"reference/com/google/android/gms/common/images/ImageManager.html", type:"class" },
-      { id:39, label:"com.google.android.gms.common.images.ImageManager.ImageReceiver", link:"reference/com/google/android/gms/common/images/ImageManager.ImageReceiver.html", type:"class" },
-      { id:40, label:"com.google.android.gms.common.images.ImageManager.OnImageLoadedListener", link:"reference/com/google/android/gms/common/images/ImageManager.OnImageLoadedListener.html", type:"class" },
-      { id:41, label:"com.google.android.gms.games", link:"reference/com/google/android/gms/games/package-summary.html", type:"package" },
-      { id:42, label:"com.google.android.gms.games.Game", link:"reference/com/google/android/gms/games/Game.html", type:"class" },
-      { id:43, label:"com.google.android.gms.games.GameBuffer", link:"reference/com/google/android/gms/games/GameBuffer.html", type:"class" },
-      { id:44, label:"com.google.android.gms.games.GameEntity", link:"reference/com/google/android/gms/games/GameEntity.html", type:"class" },
-      { id:45, label:"com.google.android.gms.games.GamesActivityResultCodes", link:"reference/com/google/android/gms/games/GamesActivityResultCodes.html", type:"class" },
-      { id:46, label:"com.google.android.gms.games.GamesClient", link:"reference/com/google/android/gms/games/GamesClient.html", type:"class" },
-      { id:47, label:"com.google.android.gms.games.GamesClient.Builder", link:"reference/com/google/android/gms/games/GamesClient.Builder.html", type:"class" },
-      { id:48, label:"com.google.android.gms.games.GamesClientSettings", link:"reference/com/google/android/gms/games/GamesClientSettings.html", type:"class" },
-      { id:49, label:"com.google.android.gms.games.OnGamesLoadedListener", link:"reference/com/google/android/gms/games/OnGamesLoadedListener.html", type:"class" },
-      { id:50, label:"com.google.android.gms.games.OnPlayersLoadedListener", link:"reference/com/google/android/gms/games/OnPlayersLoadedListener.html", type:"class" },
-      { id:51, label:"com.google.android.gms.games.OnSignOutCompleteListener", link:"reference/com/google/android/gms/games/OnSignOutCompleteListener.html", type:"class" },
-      { id:52, label:"com.google.android.gms.games.PageDirection", link:"reference/com/google/android/gms/games/PageDirection.html", type:"class" },
-      { id:53, label:"com.google.android.gms.games.Player", link:"reference/com/google/android/gms/games/Player.html", type:"class" },
-      { id:54, label:"com.google.android.gms.games.PlayerBuffer", link:"reference/com/google/android/gms/games/PlayerBuffer.html", type:"class" },
-      { id:55, label:"com.google.android.gms.games.PlayerEntity", link:"reference/com/google/android/gms/games/PlayerEntity.html", type:"class" },
-      { id:56, label:"com.google.android.gms.games.RealTimeSocket", link:"reference/com/google/android/gms/games/RealTimeSocket.html", type:"class" },
-      { id:57, label:"com.google.android.gms.games.achievement", link:"reference/com/google/android/gms/games/achievement/package-summary.html", type:"package" },
-      { id:58, label:"com.google.android.gms.games.achievement.Achievement", link:"reference/com/google/android/gms/games/achievement/Achievement.html", type:"class" },
-      { id:59, label:"com.google.android.gms.games.achievement.AchievementBuffer", link:"reference/com/google/android/gms/games/achievement/AchievementBuffer.html", type:"class" },
-      { id:60, label:"com.google.android.gms.games.achievement.OnAchievementUpdatedListener", link:"reference/com/google/android/gms/games/achievement/OnAchievementUpdatedListener.html", type:"class" },
-      { id:61, label:"com.google.android.gms.games.achievement.OnAchievementsLoadedListener", link:"reference/com/google/android/gms/games/achievement/OnAchievementsLoadedListener.html", type:"class" },
-      { id:62, label:"com.google.android.gms.games.leaderboard", link:"reference/com/google/android/gms/games/leaderboard/package-summary.html", type:"package" },
-      { id:63, label:"com.google.android.gms.games.leaderboard.Leaderboard", link:"reference/com/google/android/gms/games/leaderboard/Leaderboard.html", type:"class" },
-      { id:64, label:"com.google.android.gms.games.leaderboard.LeaderboardBuffer", link:"reference/com/google/android/gms/games/leaderboard/LeaderboardBuffer.html", type:"class" },
-      { id:65, label:"com.google.android.gms.games.leaderboard.LeaderboardScore", link:"reference/com/google/android/gms/games/leaderboard/LeaderboardScore.html", type:"class" },
-      { id:66, label:"com.google.android.gms.games.leaderboard.LeaderboardScoreBuffer", link:"reference/com/google/android/gms/games/leaderboard/LeaderboardScoreBuffer.html", type:"class" },
-      { id:67, label:"com.google.android.gms.games.leaderboard.LeaderboardVariant", link:"reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html", type:"class" },
-      { id:68, label:"com.google.android.gms.games.leaderboard.OnLeaderboardMetadataLoadedListener", link:"reference/com/google/android/gms/games/leaderboard/OnLeaderboardMetadataLoadedListener.html", type:"class" },
-      { id:69, label:"com.google.android.gms.games.leaderboard.OnLeaderboardScoresLoadedListener", link:"reference/com/google/android/gms/games/leaderboard/OnLeaderboardScoresLoadedListener.html", type:"class" },
-      { id:70, label:"com.google.android.gms.games.leaderboard.OnScoreSubmittedListener", link:"reference/com/google/android/gms/games/leaderboard/OnScoreSubmittedListener.html", type:"class" },
-      { id:71, label:"com.google.android.gms.games.leaderboard.SubmitScoreResult", link:"reference/com/google/android/gms/games/leaderboard/SubmitScoreResult.html", type:"class" },
-      { id:72, label:"com.google.android.gms.games.leaderboard.SubmitScoreResult.Result", link:"reference/com/google/android/gms/games/leaderboard/SubmitScoreResult.Result.html", type:"class" },
-      { id:73, label:"com.google.android.gms.games.multiplayer", link:"reference/com/google/android/gms/games/multiplayer/package-summary.html", type:"package" },
-      { id:74, label:"com.google.android.gms.games.multiplayer.Invitation", link:"reference/com/google/android/gms/games/multiplayer/Invitation.html", type:"class" },
-      { id:75, label:"com.google.android.gms.games.multiplayer.InvitationBuffer", link:"reference/com/google/android/gms/games/multiplayer/InvitationBuffer.html", type:"class" },
-      { id:76, label:"com.google.android.gms.games.multiplayer.InvitationEntity", link:"reference/com/google/android/gms/games/multiplayer/InvitationEntity.html", type:"class" },
-      { id:77, label:"com.google.android.gms.games.multiplayer.OnInvitationReceivedListener", link:"reference/com/google/android/gms/games/multiplayer/OnInvitationReceivedListener.html", type:"class" },
-      { id:78, label:"com.google.android.gms.games.multiplayer.OnInvitationsLoadedListener", link:"reference/com/google/android/gms/games/multiplayer/OnInvitationsLoadedListener.html", type:"class" },
-      { id:79, label:"com.google.android.gms.games.multiplayer.Participant", link:"reference/com/google/android/gms/games/multiplayer/Participant.html", type:"class" },
-      { id:80, label:"com.google.android.gms.games.multiplayer.ParticipantBuffer", link:"reference/com/google/android/gms/games/multiplayer/ParticipantBuffer.html", type:"class" },
-      { id:81, label:"com.google.android.gms.games.multiplayer.ParticipantEntity", link:"reference/com/google/android/gms/games/multiplayer/ParticipantEntity.html", type:"class" },
-      { id:82, label:"com.google.android.gms.games.multiplayer.ParticipantUtils", link:"reference/com/google/android/gms/games/multiplayer/ParticipantUtils.html", type:"class" },
-      { id:83, label:"com.google.android.gms.games.multiplayer.Participatable", link:"reference/com/google/android/gms/games/multiplayer/Participatable.html", type:"class" },
-      { id:84, label:"com.google.android.gms.games.multiplayer.realtime", link:"reference/com/google/android/gms/games/multiplayer/realtime/package-summary.html", type:"package" },
-      { id:85, label:"com.google.android.gms.games.multiplayer.realtime.RealTimeMessage", link:"reference/com/google/android/gms/games/multiplayer/realtime/RealTimeMessage.html", type:"class" },
-      { id:86, label:"com.google.android.gms.games.multiplayer.realtime.RealTimeMessageReceivedListener", link:"reference/com/google/android/gms/games/multiplayer/realtime/RealTimeMessageReceivedListener.html", type:"class" },
-      { id:87, label:"com.google.android.gms.games.multiplayer.realtime.RealTimeReliableMessageSentListener", link:"reference/com/google/android/gms/games/multiplayer/realtime/RealTimeReliableMessageSentListener.html", type:"class" },
-      { id:88, label:"com.google.android.gms.games.multiplayer.realtime.Room", link:"reference/com/google/android/gms/games/multiplayer/realtime/Room.html", type:"class" },
-      { id:89, label:"com.google.android.gms.games.multiplayer.realtime.RoomConfig", link:"reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.html", type:"class" },
-      { id:90, label:"com.google.android.gms.games.multiplayer.realtime.RoomConfig.Builder", link:"reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.Builder.html", type:"class" },
-      { id:91, label:"com.google.android.gms.games.multiplayer.realtime.RoomEntity", link:"reference/com/google/android/gms/games/multiplayer/realtime/RoomEntity.html", type:"class" },
-      { id:92, label:"com.google.android.gms.games.multiplayer.realtime.RoomStatusUpdateListener", link:"reference/com/google/android/gms/games/multiplayer/realtime/RoomStatusUpdateListener.html", type:"class" },
-      { id:93, label:"com.google.android.gms.games.multiplayer.realtime.RoomUpdateListener", link:"reference/com/google/android/gms/games/multiplayer/realtime/RoomUpdateListener.html", type:"class" },
-      { id:94, label:"com.google.android.gms.gcm", link:"reference/com/google/android/gms/gcm/package-summary.html", type:"package" },
-      { id:95, label:"com.google.android.gms.gcm.GoogleCloudMessaging", link:"reference/com/google/android/gms/gcm/GoogleCloudMessaging.html", type:"class" },
-      { id:96, label:"com.google.android.gms.location", link:"reference/com/google/android/gms/location/package-summary.html", type:"package" },
-      { id:97, label:"com.google.android.gms.location.ActivityRecognitionClient", link:"reference/com/google/android/gms/location/ActivityRecognitionClient.html", type:"class" },
-      { id:98, label:"com.google.android.gms.location.ActivityRecognitionResult", link:"reference/com/google/android/gms/location/ActivityRecognitionResult.html", type:"class" },
-      { id:99, label:"com.google.android.gms.location.DetectedActivity", link:"reference/com/google/android/gms/location/DetectedActivity.html", type:"class" },
-      { id:100, label:"com.google.android.gms.location.Geofence", link:"reference/com/google/android/gms/location/Geofence.html", type:"class" },
-      { id:101, label:"com.google.android.gms.location.Geofence.Builder", link:"reference/com/google/android/gms/location/Geofence.Builder.html", type:"class" },
-      { id:102, label:"com.google.android.gms.location.LocationClient", link:"reference/com/google/android/gms/location/LocationClient.html", type:"class" },
-      { id:103, label:"com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener", link:"reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html", type:"class" },
-      { id:104, label:"com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener", link:"reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html", type:"class" },
-      { id:105, label:"com.google.android.gms.location.LocationListener", link:"reference/com/google/android/gms/location/LocationListener.html", type:"class" },
-      { id:106, label:"com.google.android.gms.location.LocationRequest", link:"reference/com/google/android/gms/location/LocationRequest.html", type:"class" },
-      { id:107, label:"com.google.android.gms.location.LocationStatusCodes", link:"reference/com/google/android/gms/location/LocationStatusCodes.html", type:"class" },
-      { id:108, label:"com.google.android.gms.maps", link:"reference/com/google/android/gms/maps/package-summary.html", type:"package" },
-      { id:109, label:"com.google.android.gms.maps.CameraUpdate", link:"reference/com/google/android/gms/maps/CameraUpdate.html", type:"class" },
-      { id:110, label:"com.google.android.gms.maps.CameraUpdateFactory", link:"reference/com/google/android/gms/maps/CameraUpdateFactory.html", type:"class" },
-      { id:111, label:"com.google.android.gms.maps.GoogleMap", link:"reference/com/google/android/gms/maps/GoogleMap.html", type:"class" },
-      { id:112, label:"com.google.android.gms.maps.GoogleMap.CancelableCallback", link:"reference/com/google/android/gms/maps/GoogleMap.CancelableCallback.html", type:"class" },
-      { id:113, label:"com.google.android.gms.maps.GoogleMap.InfoWindowAdapter", link:"reference/com/google/android/gms/maps/GoogleMap.InfoWindowAdapter.html", type:"class" },
-      { id:114, label:"com.google.android.gms.maps.GoogleMap.OnCameraChangeListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnCameraChangeListener.html", type:"class" },
-      { id:115, label:"com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnInfoWindowClickListener.html", type:"class" },
-      { id:116, label:"com.google.android.gms.maps.GoogleMap.OnMapClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMapClickListener.html", type:"class" },
-      { id:117, label:"com.google.android.gms.maps.GoogleMap.OnMapLongClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMapLongClickListener.html", type:"class" },
-      { id:118, label:"com.google.android.gms.maps.GoogleMap.OnMarkerClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMarkerClickListener.html", type:"class" },
-      { id:119, label:"com.google.android.gms.maps.GoogleMap.OnMarkerDragListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMarkerDragListener.html", type:"class" },
-      { id:120, label:"com.google.android.gms.maps.GoogleMap.OnMyLocationChangeListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMyLocationChangeListener.html", type:"class" },
-      { id:121, label:"com.google.android.gms.maps.GoogleMapOptions", link:"reference/com/google/android/gms/maps/GoogleMapOptions.html", type:"class" },
-      { id:122, label:"com.google.android.gms.maps.LocationSource", link:"reference/com/google/android/gms/maps/LocationSource.html", type:"class" },
-      { id:123, label:"com.google.android.gms.maps.LocationSource.OnLocationChangedListener", link:"reference/com/google/android/gms/maps/LocationSource.OnLocationChangedListener.html", type:"class" },
-      { id:124, label:"com.google.android.gms.maps.MapFragment", link:"reference/com/google/android/gms/maps/MapFragment.html", type:"class" },
-      { id:125, label:"com.google.android.gms.maps.MapView", link:"reference/com/google/android/gms/maps/MapView.html", type:"class" },
-      { id:126, label:"com.google.android.gms.maps.MapsInitializer", link:"reference/com/google/android/gms/maps/MapsInitializer.html", type:"class" },
-      { id:127, label:"com.google.android.gms.maps.Projection", link:"reference/com/google/android/gms/maps/Projection.html", type:"class" },
-      { id:128, label:"com.google.android.gms.maps.SupportMapFragment", link:"reference/com/google/android/gms/maps/SupportMapFragment.html", type:"class" },
-      { id:129, label:"com.google.android.gms.maps.UiSettings", link:"reference/com/google/android/gms/maps/UiSettings.html", type:"class" },
-      { id:130, label:"com.google.android.gms.maps.model", link:"reference/com/google/android/gms/maps/model/package-summary.html", type:"package" },
-      { id:131, label:"com.google.android.gms.maps.model.BitmapDescriptor", link:"reference/com/google/android/gms/maps/model/BitmapDescriptor.html", type:"class" },
-      { id:132, label:"com.google.android.gms.maps.model.BitmapDescriptorFactory", link:"reference/com/google/android/gms/maps/model/BitmapDescriptorFactory.html", type:"class" },
-      { id:133, label:"com.google.android.gms.maps.model.CameraPosition", link:"reference/com/google/android/gms/maps/model/CameraPosition.html", type:"class" },
-      { id:134, label:"com.google.android.gms.maps.model.CameraPosition.Builder", link:"reference/com/google/android/gms/maps/model/CameraPosition.Builder.html", type:"class" },
-      { id:135, label:"com.google.android.gms.maps.model.Circle", link:"reference/com/google/android/gms/maps/model/Circle.html", type:"class" },
-      { id:136, label:"com.google.android.gms.maps.model.CircleOptions", link:"reference/com/google/android/gms/maps/model/CircleOptions.html", type:"class" },
-      { id:137, label:"com.google.android.gms.maps.model.GroundOverlay", link:"reference/com/google/android/gms/maps/model/GroundOverlay.html", type:"class" },
-      { id:138, label:"com.google.android.gms.maps.model.GroundOverlayOptions", link:"reference/com/google/android/gms/maps/model/GroundOverlayOptions.html", type:"class" },
-      { id:139, label:"com.google.android.gms.maps.model.LatLng", link:"reference/com/google/android/gms/maps/model/LatLng.html", type:"class" },
-      { id:140, label:"com.google.android.gms.maps.model.LatLngBounds", link:"reference/com/google/android/gms/maps/model/LatLngBounds.html", type:"class" },
-      { id:141, label:"com.google.android.gms.maps.model.LatLngBounds.Builder", link:"reference/com/google/android/gms/maps/model/LatLngBounds.Builder.html", type:"class" },
-      { id:142, label:"com.google.android.gms.maps.model.Marker", link:"reference/com/google/android/gms/maps/model/Marker.html", type:"class" },
-      { id:143, label:"com.google.android.gms.maps.model.MarkerOptions", link:"reference/com/google/android/gms/maps/model/MarkerOptions.html", type:"class" },
-      { id:144, label:"com.google.android.gms.maps.model.Polygon", link:"reference/com/google/android/gms/maps/model/Polygon.html", type:"class" },
-      { id:145, label:"com.google.android.gms.maps.model.PolygonOptions", link:"reference/com/google/android/gms/maps/model/PolygonOptions.html", type:"class" },
-      { id:146, label:"com.google.android.gms.maps.model.Polyline", link:"reference/com/google/android/gms/maps/model/Polyline.html", type:"class" },
-      { id:147, label:"com.google.android.gms.maps.model.PolylineOptions", link:"reference/com/google/android/gms/maps/model/PolylineOptions.html", type:"class" },
-      { id:148, label:"com.google.android.gms.maps.model.RuntimeRemoteException", link:"reference/com/google/android/gms/maps/model/RuntimeRemoteException.html", type:"class" },
-      { id:149, label:"com.google.android.gms.maps.model.Tile", link:"reference/com/google/android/gms/maps/model/Tile.html", type:"class" },
-      { id:150, label:"com.google.android.gms.maps.model.TileOverlay", link:"reference/com/google/android/gms/maps/model/TileOverlay.html", type:"class" },
-      { id:151, label:"com.google.android.gms.maps.model.TileOverlayOptions", link:"reference/com/google/android/gms/maps/model/TileOverlayOptions.html", type:"class" },
-      { id:152, label:"com.google.android.gms.maps.model.TileProvider", link:"reference/com/google/android/gms/maps/model/TileProvider.html", type:"class" },
-      { id:153, label:"com.google.android.gms.maps.model.UrlTileProvider", link:"reference/com/google/android/gms/maps/model/UrlTileProvider.html", type:"class" },
-      { id:154, label:"com.google.android.gms.maps.model.VisibleRegion", link:"reference/com/google/android/gms/maps/model/VisibleRegion.html", type:"class" },
-      { id:155, label:"com.google.android.gms.panorama", link:"reference/com/google/android/gms/panorama/package-summary.html", type:"package" },
-      { id:156, label:"com.google.android.gms.panorama.PanoramaClient", link:"reference/com/google/android/gms/panorama/PanoramaClient.html", type:"class" },
-      { id:157, label:"com.google.android.gms.panorama.PanoramaClient.OnPanoramaInfoLoadedListener", link:"reference/com/google/android/gms/panorama/PanoramaClient.OnPanoramaInfoLoadedListener.html", type:"class" },
-      { id:158, label:"com.google.android.gms.plus", link:"reference/com/google/android/gms/plus/package-summary.html", type:"package" },
-      { id:159, label:"com.google.android.gms.plus.GooglePlusUtil", link:"reference/com/google/android/gms/plus/GooglePlusUtil.html", type:"class" },
-      { id:160, label:"com.google.android.gms.plus.PlusClient", link:"reference/com/google/android/gms/plus/PlusClient.html", type:"class" },
-      { id:161, label:"com.google.android.gms.plus.PlusClient.Builder", link:"reference/com/google/android/gms/plus/PlusClient.Builder.html", type:"class" },
-      { id:162, label:"com.google.android.gms.plus.PlusClient.OnAccessRevokedListener", link:"reference/com/google/android/gms/plus/PlusClient.OnAccessRevokedListener.html", type:"class" },
-      { id:163, label:"com.google.android.gms.plus.PlusClient.OnMomentsLoadedListener", link:"reference/com/google/android/gms/plus/PlusClient.OnMomentsLoadedListener.html", type:"class" },
-      { id:164, label:"com.google.android.gms.plus.PlusClient.OnPeopleLoadedListener", link:"reference/com/google/android/gms/plus/PlusClient.OnPeopleLoadedListener.html", type:"class" },
-      { id:165, label:"com.google.android.gms.plus.PlusClient.OnPersonLoadedListener", link:"reference/com/google/android/gms/plus/PlusClient.OnPersonLoadedListener.html", type:"class" },
-      { id:166, label:"com.google.android.gms.plus.PlusOneButton", link:"reference/com/google/android/gms/plus/PlusOneButton.html", type:"class" },
-      { id:167, label:"com.google.android.gms.plus.PlusOneButton.OnPlusOneClickListener", link:"reference/com/google/android/gms/plus/PlusOneButton.OnPlusOneClickListener.html", type:"class" },
-      { id:168, label:"com.google.android.gms.plus.PlusShare", link:"reference/com/google/android/gms/plus/PlusShare.html", type:"class" },
-      { id:169, label:"com.google.android.gms.plus.PlusShare.Builder", link:"reference/com/google/android/gms/plus/PlusShare.Builder.html", type:"class" },
-      { id:170, label:"com.google.android.gms.plus.model.moments", link:"reference/com/google/android/gms/plus/model/moments/package-summary.html", type:"package" },
-      { id:171, label:"com.google.android.gms.plus.model.moments.ItemScope", link:"reference/com/google/android/gms/plus/model/moments/ItemScope.html", type:"class" },
-      { id:172, label:"com.google.android.gms.plus.model.moments.ItemScope.Builder", link:"reference/com/google/android/gms/plus/model/moments/ItemScope.Builder.html", type:"class" },
-      { id:173, label:"com.google.android.gms.plus.model.moments.Moment", link:"reference/com/google/android/gms/plus/model/moments/Moment.html", type:"class" },
-      { id:174, label:"com.google.android.gms.plus.model.moments.Moment.Builder", link:"reference/com/google/android/gms/plus/model/moments/Moment.Builder.html", type:"class" },
-      { id:175, label:"com.google.android.gms.plus.model.moments.MomentBuffer", link:"reference/com/google/android/gms/plus/model/moments/MomentBuffer.html", type:"class" },
-      { id:176, label:"com.google.android.gms.plus.model.people", link:"reference/com/google/android/gms/plus/model/people/package-summary.html", type:"package" },
-      { id:177, label:"com.google.android.gms.plus.model.people.Person", link:"reference/com/google/android/gms/plus/model/people/Person.html", type:"class" },
-      { id:178, label:"com.google.android.gms.plus.model.people.Person.AgeRange", link:"reference/com/google/android/gms/plus/model/people/Person.AgeRange.html", type:"class" },
-      { id:179, label:"com.google.android.gms.plus.model.people.Person.Collection", link:"reference/com/google/android/gms/plus/model/people/Person.Collection.html", type:"class" },
-      { id:180, label:"com.google.android.gms.plus.model.people.Person.Cover", link:"reference/com/google/android/gms/plus/model/people/Person.Cover.html", type:"class" },
-      { id:181, label:"com.google.android.gms.plus.model.people.Person.Cover.CoverInfo", link:"reference/com/google/android/gms/plus/model/people/Person.Cover.CoverInfo.html", type:"class" },
-      { id:182, label:"com.google.android.gms.plus.model.people.Person.Cover.CoverPhoto", link:"reference/com/google/android/gms/plus/model/people/Person.Cover.CoverPhoto.html", type:"class" },
-      { id:183, label:"com.google.android.gms.plus.model.people.Person.Cover.Layout", link:"reference/com/google/android/gms/plus/model/people/Person.Cover.Layout.html", type:"class" },
-      { id:184, label:"com.google.android.gms.plus.model.people.Person.Emails", link:"reference/com/google/android/gms/plus/model/people/Person.Emails.html", type:"class" },
-      { id:185, label:"com.google.android.gms.plus.model.people.Person.Emails.Type", link:"reference/com/google/android/gms/plus/model/people/Person.Emails.Type.html", type:"class" },
-      { id:186, label:"com.google.android.gms.plus.model.people.Person.Gender", link:"reference/com/google/android/gms/plus/model/people/Person.Gender.html", type:"class" },
-      { id:187, label:"com.google.android.gms.plus.model.people.Person.Image", link:"reference/com/google/android/gms/plus/model/people/Person.Image.html", type:"class" },
-      { id:188, label:"com.google.android.gms.plus.model.people.Person.Name", link:"reference/com/google/android/gms/plus/model/people/Person.Name.html", type:"class" },
-      { id:189, label:"com.google.android.gms.plus.model.people.Person.ObjectType", link:"reference/com/google/android/gms/plus/model/people/Person.ObjectType.html", type:"class" },
-      { id:190, label:"com.google.android.gms.plus.model.people.Person.OrderBy", link:"reference/com/google/android/gms/plus/model/people/Person.OrderBy.html", type:"class" },
-      { id:191, label:"com.google.android.gms.plus.model.people.Person.Organizations", link:"reference/com/google/android/gms/plus/model/people/Person.Organizations.html", type:"class" },
-      { id:192, label:"com.google.android.gms.plus.model.people.Person.Organizations.Type", link:"reference/com/google/android/gms/plus/model/people/Person.Organizations.Type.html", type:"class" },
-      { id:193, label:"com.google.android.gms.plus.model.people.Person.PlacesLived", link:"reference/com/google/android/gms/plus/model/people/Person.PlacesLived.html", type:"class" },
-      { id:194, label:"com.google.android.gms.plus.model.people.Person.RelationshipStatus", link:"reference/com/google/android/gms/plus/model/people/Person.RelationshipStatus.html", type:"class" },
-      { id:195, label:"com.google.android.gms.plus.model.people.Person.Urls", link:"reference/com/google/android/gms/plus/model/people/Person.Urls.html", type:"class" },
-      { id:196, label:"com.google.android.gms.plus.model.people.Person.Urls.Type", link:"reference/com/google/android/gms/plus/model/people/Person.Urls.Type.html", type:"class" },
-      { id:197, label:"com.google.android.gms.plus.model.people.PersonBuffer", link:"reference/com/google/android/gms/plus/model/people/PersonBuffer.html", type:"class" }
+      { id:0, label:"com.google.android.gms", link:"reference/com/google/android/gms/package-summary.html", type:"package", deprecated:"false" },
+      { id:1, label:"com.google.android.gms.R", link:"reference/com/google/android/gms/R.html", type:"class", deprecated:"false" },
+      { id:2, label:"com.google.android.gms.R.attr", link:"reference/com/google/android/gms/R.attr.html", type:"class", deprecated:"false" },
+      { id:3, label:"com.google.android.gms.R.color", link:"reference/com/google/android/gms/R.color.html", type:"class", deprecated:"false" },
+      { id:4, label:"com.google.android.gms.R.drawable", link:"reference/com/google/android/gms/R.drawable.html", type:"class", deprecated:"false" },
+      { id:5, label:"com.google.android.gms.R.id", link:"reference/com/google/android/gms/R.id.html", type:"class", deprecated:"false" },
+      { id:6, label:"com.google.android.gms.R.string", link:"reference/com/google/android/gms/R.string.html", type:"class", deprecated:"false" },
+      { id:7, label:"com.google.android.gms.R.styleable", link:"reference/com/google/android/gms/R.styleable.html", type:"class", deprecated:"false" },
+      { id:8, label:"com.google.android.gms.appstate", link:"reference/com/google/android/gms/appstate/package-summary.html", type:"package", deprecated:"false" },
+      { id:9, label:"com.google.android.gms.appstate.AppState", link:"reference/com/google/android/gms/appstate/AppState.html", type:"class", deprecated:"false" },
+      { id:10, label:"com.google.android.gms.appstate.AppStateBuffer", link:"reference/com/google/android/gms/appstate/AppStateBuffer.html", type:"class", deprecated:"false" },
+      { id:11, label:"com.google.android.gms.appstate.AppStateClient", link:"reference/com/google/android/gms/appstate/AppStateClient.html", type:"class", deprecated:"false" },
+      { id:12, label:"com.google.android.gms.appstate.AppStateClient.Builder", link:"reference/com/google/android/gms/appstate/AppStateClient.Builder.html", type:"class", deprecated:"false" },
+      { id:13, label:"com.google.android.gms.appstate.OnSignOutCompleteListener", link:"reference/com/google/android/gms/appstate/OnSignOutCompleteListener.html", type:"class", deprecated:"false" },
+      { id:14, label:"com.google.android.gms.appstate.OnStateDeletedListener", link:"reference/com/google/android/gms/appstate/OnStateDeletedListener.html", type:"class", deprecated:"false" },
+      { id:15, label:"com.google.android.gms.appstate.OnStateListLoadedListener", link:"reference/com/google/android/gms/appstate/OnStateListLoadedListener.html", type:"class", deprecated:"false" },
+      { id:16, label:"com.google.android.gms.appstate.OnStateLoadedListener", link:"reference/com/google/android/gms/appstate/OnStateLoadedListener.html", type:"class", deprecated:"false" },
+      { id:17, label:"com.google.android.gms.auth", link:"reference/com/google/android/gms/auth/package-summary.html", type:"package", deprecated:"false" },
+      { id:18, label:"com.google.android.gms.auth.GoogleAuthException", link:"reference/com/google/android/gms/auth/GoogleAuthException.html", type:"class", deprecated:"false" },
+      { id:19, label:"com.google.android.gms.auth.GoogleAuthUtil", link:"reference/com/google/android/gms/auth/GoogleAuthUtil.html", type:"class", deprecated:"false" },
+      { id:20, label:"com.google.android.gms.auth.GooglePlayServicesAvailabilityException", link:"reference/com/google/android/gms/auth/GooglePlayServicesAvailabilityException.html", type:"class", deprecated:"false" },
+      { id:21, label:"com.google.android.gms.auth.UserRecoverableAuthException", link:"reference/com/google/android/gms/auth/UserRecoverableAuthException.html", type:"class", deprecated:"false" },
+      { id:22, label:"com.google.android.gms.auth.UserRecoverableNotifiedException", link:"reference/com/google/android/gms/auth/UserRecoverableNotifiedException.html", type:"class", deprecated:"false" },
+      { id:23, label:"com.google.android.gms.common", link:"reference/com/google/android/gms/common/package-summary.html", type:"package", deprecated:"false" },
+      { id:24, label:"com.google.android.gms.common.AccountPicker", link:"reference/com/google/android/gms/common/AccountPicker.html", type:"class", deprecated:"false" },
+      { id:25, label:"com.google.android.gms.common.ConnectionResult", link:"reference/com/google/android/gms/common/ConnectionResult.html", type:"class", deprecated:"false" },
+      { id:26, label:"com.google.android.gms.common.GooglePlayServicesClient", link:"reference/com/google/android/gms/common/GooglePlayServicesClient.html", type:"class", deprecated:"false" },
+      { id:27, label:"com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks", link:"reference/com/google/android/gms/common/GooglePlayServicesClient.ConnectionCallbacks.html", type:"class", deprecated:"false" },
+      { id:28, label:"com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener", link:"reference/com/google/android/gms/common/GooglePlayServicesClient.OnConnectionFailedListener.html", type:"class", deprecated:"false" },
+      { id:29, label:"com.google.android.gms.common.GooglePlayServicesNotAvailableException", link:"reference/com/google/android/gms/common/GooglePlayServicesNotAvailableException.html", type:"class", deprecated:"false" },
+      { id:30, label:"com.google.android.gms.common.GooglePlayServicesUtil", link:"reference/com/google/android/gms/common/GooglePlayServicesUtil.html", type:"class", deprecated:"false" },
+      { id:31, label:"com.google.android.gms.common.Scopes", link:"reference/com/google/android/gms/common/Scopes.html", type:"class", deprecated:"false" },
+      { id:32, label:"com.google.android.gms.common.SignInButton", link:"reference/com/google/android/gms/common/SignInButton.html", type:"class", deprecated:"false" },
+      { id:33, label:"com.google.android.gms.common.data", link:"reference/com/google/android/gms/common/data/package-summary.html", type:"package", deprecated:"false" },
+      { id:34, label:"com.google.android.gms.common.data.DataBuffer", link:"reference/com/google/android/gms/common/data/DataBuffer.html", type:"class", deprecated:"false" },
+      { id:35, label:"com.google.android.gms.common.data.DataBufferUtils", link:"reference/com/google/android/gms/common/data/DataBufferUtils.html", type:"class", deprecated:"false" },
+      { id:36, label:"com.google.android.gms.common.data.Freezable", link:"reference/com/google/android/gms/common/data/Freezable.html", type:"class", deprecated:"false" },
+      { id:37, label:"com.google.android.gms.common.images", link:"reference/com/google/android/gms/common/images/package-summary.html", type:"package", deprecated:"false" },
+      { id:38, label:"com.google.android.gms.common.images.ImageManager", link:"reference/com/google/android/gms/common/images/ImageManager.html", type:"class", deprecated:"false" },
+      { id:39, label:"com.google.android.gms.common.images.ImageManager.OnImageLoadedListener", link:"reference/com/google/android/gms/common/images/ImageManager.OnImageLoadedListener.html", type:"class", deprecated:"false" },
+      { id:40, label:"com.google.android.gms.games", link:"reference/com/google/android/gms/games/package-summary.html", type:"package", deprecated:"false" },
+      { id:41, label:"com.google.android.gms.games.Game", link:"reference/com/google/android/gms/games/Game.html", type:"class", deprecated:"false" },
+      { id:42, label:"com.google.android.gms.games.GameBuffer", link:"reference/com/google/android/gms/games/GameBuffer.html", type:"class", deprecated:"false" },
+      { id:43, label:"com.google.android.gms.games.GameEntity", link:"reference/com/google/android/gms/games/GameEntity.html", type:"class", deprecated:"false" },
+      { id:44, label:"com.google.android.gms.games.GamesActivityResultCodes", link:"reference/com/google/android/gms/games/GamesActivityResultCodes.html", type:"class", deprecated:"false" },
+      { id:45, label:"com.google.android.gms.games.GamesClient", link:"reference/com/google/android/gms/games/GamesClient.html", type:"class", deprecated:"false" },
+      { id:46, label:"com.google.android.gms.games.GamesClient.Builder", link:"reference/com/google/android/gms/games/GamesClient.Builder.html", type:"class", deprecated:"false" },
+      { id:47, label:"com.google.android.gms.games.OnGamesLoadedListener", link:"reference/com/google/android/gms/games/OnGamesLoadedListener.html", type:"class", deprecated:"false" },
+      { id:48, label:"com.google.android.gms.games.OnPlayersLoadedListener", link:"reference/com/google/android/gms/games/OnPlayersLoadedListener.html", type:"class", deprecated:"false" },
+      { id:49, label:"com.google.android.gms.games.OnSignOutCompleteListener", link:"reference/com/google/android/gms/games/OnSignOutCompleteListener.html", type:"class", deprecated:"false" },
+      { id:50, label:"com.google.android.gms.games.PageDirection", link:"reference/com/google/android/gms/games/PageDirection.html", type:"class", deprecated:"false" },
+      { id:51, label:"com.google.android.gms.games.Player", link:"reference/com/google/android/gms/games/Player.html", type:"class", deprecated:"false" },
+      { id:52, label:"com.google.android.gms.games.PlayerBuffer", link:"reference/com/google/android/gms/games/PlayerBuffer.html", type:"class", deprecated:"false" },
+      { id:53, label:"com.google.android.gms.games.PlayerEntity", link:"reference/com/google/android/gms/games/PlayerEntity.html", type:"class", deprecated:"false" },
+      { id:54, label:"com.google.android.gms.games.RealTimeSocket", link:"reference/com/google/android/gms/games/RealTimeSocket.html", type:"class", deprecated:"false" },
+      { id:55, label:"com.google.android.gms.games.achievement", link:"reference/com/google/android/gms/games/achievement/package-summary.html", type:"package", deprecated:"false" },
+      { id:56, label:"com.google.android.gms.games.achievement.Achievement", link:"reference/com/google/android/gms/games/achievement/Achievement.html", type:"class", deprecated:"false" },
+      { id:57, label:"com.google.android.gms.games.achievement.AchievementBuffer", link:"reference/com/google/android/gms/games/achievement/AchievementBuffer.html", type:"class", deprecated:"false" },
+      { id:58, label:"com.google.android.gms.games.achievement.OnAchievementUpdatedListener", link:"reference/com/google/android/gms/games/achievement/OnAchievementUpdatedListener.html", type:"class", deprecated:"false" },
+      { id:59, label:"com.google.android.gms.games.achievement.OnAchievementsLoadedListener", link:"reference/com/google/android/gms/games/achievement/OnAchievementsLoadedListener.html", type:"class", deprecated:"false" },
+      { id:60, label:"com.google.android.gms.games.leaderboard", link:"reference/com/google/android/gms/games/leaderboard/package-summary.html", type:"package", deprecated:"false" },
+      { id:61, label:"com.google.android.gms.games.leaderboard.Leaderboard", link:"reference/com/google/android/gms/games/leaderboard/Leaderboard.html", type:"class", deprecated:"false" },
+      { id:62, label:"com.google.android.gms.games.leaderboard.LeaderboardBuffer", link:"reference/com/google/android/gms/games/leaderboard/LeaderboardBuffer.html", type:"class", deprecated:"false" },
+      { id:63, label:"com.google.android.gms.games.leaderboard.LeaderboardScore", link:"reference/com/google/android/gms/games/leaderboard/LeaderboardScore.html", type:"class", deprecated:"false" },
+      { id:64, label:"com.google.android.gms.games.leaderboard.LeaderboardScoreBuffer", link:"reference/com/google/android/gms/games/leaderboard/LeaderboardScoreBuffer.html", type:"class", deprecated:"false" },
+      { id:65, label:"com.google.android.gms.games.leaderboard.LeaderboardVariant", link:"reference/com/google/android/gms/games/leaderboard/LeaderboardVariant.html", type:"class", deprecated:"false" },
+      { id:66, label:"com.google.android.gms.games.leaderboard.OnLeaderboardMetadataLoadedListener", link:"reference/com/google/android/gms/games/leaderboard/OnLeaderboardMetadataLoadedListener.html", type:"class", deprecated:"false" },
+      { id:67, label:"com.google.android.gms.games.leaderboard.OnLeaderboardScoresLoadedListener", link:"reference/com/google/android/gms/games/leaderboard/OnLeaderboardScoresLoadedListener.html", type:"class", deprecated:"false" },
+      { id:68, label:"com.google.android.gms.games.leaderboard.OnScoreSubmittedListener", link:"reference/com/google/android/gms/games/leaderboard/OnScoreSubmittedListener.html", type:"class", deprecated:"false" },
+      { id:69, label:"com.google.android.gms.games.leaderboard.SubmitScoreResult", link:"reference/com/google/android/gms/games/leaderboard/SubmitScoreResult.html", type:"class", deprecated:"false" },
+      { id:70, label:"com.google.android.gms.games.leaderboard.SubmitScoreResult.Result", link:"reference/com/google/android/gms/games/leaderboard/SubmitScoreResult.Result.html", type:"class", deprecated:"false" },
+      { id:71, label:"com.google.android.gms.games.multiplayer", link:"reference/com/google/android/gms/games/multiplayer/package-summary.html", type:"package", deprecated:"false" },
+      { id:72, label:"com.google.android.gms.games.multiplayer.Invitation", link:"reference/com/google/android/gms/games/multiplayer/Invitation.html", type:"class", deprecated:"false" },
+      { id:73, label:"com.google.android.gms.games.multiplayer.InvitationBuffer", link:"reference/com/google/android/gms/games/multiplayer/InvitationBuffer.html", type:"class", deprecated:"false" },
+      { id:74, label:"com.google.android.gms.games.multiplayer.InvitationEntity", link:"reference/com/google/android/gms/games/multiplayer/InvitationEntity.html", type:"class", deprecated:"false" },
+      { id:75, label:"com.google.android.gms.games.multiplayer.OnInvitationReceivedListener", link:"reference/com/google/android/gms/games/multiplayer/OnInvitationReceivedListener.html", type:"class", deprecated:"false" },
+      { id:76, label:"com.google.android.gms.games.multiplayer.OnInvitationsLoadedListener", link:"reference/com/google/android/gms/games/multiplayer/OnInvitationsLoadedListener.html", type:"class", deprecated:"false" },
+      { id:77, label:"com.google.android.gms.games.multiplayer.Participant", link:"reference/com/google/android/gms/games/multiplayer/Participant.html", type:"class", deprecated:"false" },
+      { id:78, label:"com.google.android.gms.games.multiplayer.ParticipantBuffer", link:"reference/com/google/android/gms/games/multiplayer/ParticipantBuffer.html", type:"class", deprecated:"false" },
+      { id:79, label:"com.google.android.gms.games.multiplayer.ParticipantEntity", link:"reference/com/google/android/gms/games/multiplayer/ParticipantEntity.html", type:"class", deprecated:"false" },
+      { id:80, label:"com.google.android.gms.games.multiplayer.ParticipantUtils", link:"reference/com/google/android/gms/games/multiplayer/ParticipantUtils.html", type:"class", deprecated:"false" },
+      { id:81, label:"com.google.android.gms.games.multiplayer.Participatable", link:"reference/com/google/android/gms/games/multiplayer/Participatable.html", type:"class", deprecated:"false" },
+      { id:82, label:"com.google.android.gms.games.multiplayer.realtime", link:"reference/com/google/android/gms/games/multiplayer/realtime/package-summary.html", type:"package", deprecated:"false" },
+      { id:83, label:"com.google.android.gms.games.multiplayer.realtime.RealTimeMessage", link:"reference/com/google/android/gms/games/multiplayer/realtime/RealTimeMessage.html", type:"class", deprecated:"false" },
+      { id:84, label:"com.google.android.gms.games.multiplayer.realtime.RealTimeMessageReceivedListener", link:"reference/com/google/android/gms/games/multiplayer/realtime/RealTimeMessageReceivedListener.html", type:"class", deprecated:"false" },
+      { id:85, label:"com.google.android.gms.games.multiplayer.realtime.RealTimeReliableMessageSentListener", link:"reference/com/google/android/gms/games/multiplayer/realtime/RealTimeReliableMessageSentListener.html", type:"class", deprecated:"false" },
+      { id:86, label:"com.google.android.gms.games.multiplayer.realtime.Room", link:"reference/com/google/android/gms/games/multiplayer/realtime/Room.html", type:"class", deprecated:"false" },
+      { id:87, label:"com.google.android.gms.games.multiplayer.realtime.RoomConfig", link:"reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.html", type:"class", deprecated:"false" },
+      { id:88, label:"com.google.android.gms.games.multiplayer.realtime.RoomConfig.Builder", link:"reference/com/google/android/gms/games/multiplayer/realtime/RoomConfig.Builder.html", type:"class", deprecated:"false" },
+      { id:89, label:"com.google.android.gms.games.multiplayer.realtime.RoomEntity", link:"reference/com/google/android/gms/games/multiplayer/realtime/RoomEntity.html", type:"class", deprecated:"false" },
+      { id:90, label:"com.google.android.gms.games.multiplayer.realtime.RoomStatusUpdateListener", link:"reference/com/google/android/gms/games/multiplayer/realtime/RoomStatusUpdateListener.html", type:"class", deprecated:"false" },
+      { id:91, label:"com.google.android.gms.games.multiplayer.realtime.RoomUpdateListener", link:"reference/com/google/android/gms/games/multiplayer/realtime/RoomUpdateListener.html", type:"class", deprecated:"false" },
+      { id:92, label:"com.google.android.gms.gcm", link:"reference/com/google/android/gms/gcm/package-summary.html", type:"package", deprecated:"false" },
+      { id:93, label:"com.google.android.gms.gcm.GoogleCloudMessaging", link:"reference/com/google/android/gms/gcm/GoogleCloudMessaging.html", type:"class", deprecated:"false" },
+      { id:94, label:"com.google.android.gms.location", link:"reference/com/google/android/gms/location/package-summary.html", type:"package", deprecated:"false" },
+      { id:95, label:"com.google.android.gms.location.ActivityRecognitionClient", link:"reference/com/google/android/gms/location/ActivityRecognitionClient.html", type:"class", deprecated:"false" },
+      { id:96, label:"com.google.android.gms.location.ActivityRecognitionResult", link:"reference/com/google/android/gms/location/ActivityRecognitionResult.html", type:"class", deprecated:"false" },
+      { id:97, label:"com.google.android.gms.location.DetectedActivity", link:"reference/com/google/android/gms/location/DetectedActivity.html", type:"class", deprecated:"false" },
+      { id:98, label:"com.google.android.gms.location.Geofence", link:"reference/com/google/android/gms/location/Geofence.html", type:"class", deprecated:"false" },
+      { id:99, label:"com.google.android.gms.location.Geofence.Builder", link:"reference/com/google/android/gms/location/Geofence.Builder.html", type:"class", deprecated:"false" },
+      { id:100, label:"com.google.android.gms.location.LocationClient", link:"reference/com/google/android/gms/location/LocationClient.html", type:"class", deprecated:"false" },
+      { id:101, label:"com.google.android.gms.location.LocationClient.OnAddGeofencesResultListener", link:"reference/com/google/android/gms/location/LocationClient.OnAddGeofencesResultListener.html", type:"class", deprecated:"false" },
+      { id:102, label:"com.google.android.gms.location.LocationClient.OnRemoveGeofencesResultListener", link:"reference/com/google/android/gms/location/LocationClient.OnRemoveGeofencesResultListener.html", type:"class", deprecated:"false" },
+      { id:103, label:"com.google.android.gms.location.LocationListener", link:"reference/com/google/android/gms/location/LocationListener.html", type:"class", deprecated:"false" },
+      { id:104, label:"com.google.android.gms.location.LocationRequest", link:"reference/com/google/android/gms/location/LocationRequest.html", type:"class", deprecated:"false" },
+      { id:105, label:"com.google.android.gms.location.LocationStatusCodes", link:"reference/com/google/android/gms/location/LocationStatusCodes.html", type:"class", deprecated:"false" },
+      { id:106, label:"com.google.android.gms.maps", link:"reference/com/google/android/gms/maps/package-summary.html", type:"package", deprecated:"false" },
+      { id:107, label:"com.google.android.gms.maps.CameraUpdate", link:"reference/com/google/android/gms/maps/CameraUpdate.html", type:"class", deprecated:"false" },
+      { id:108, label:"com.google.android.gms.maps.CameraUpdateFactory", link:"reference/com/google/android/gms/maps/CameraUpdateFactory.html", type:"class", deprecated:"false" },
+      { id:109, label:"com.google.android.gms.maps.GoogleMap", link:"reference/com/google/android/gms/maps/GoogleMap.html", type:"class", deprecated:"false" },
+      { id:110, label:"com.google.android.gms.maps.GoogleMap.CancelableCallback", link:"reference/com/google/android/gms/maps/GoogleMap.CancelableCallback.html", type:"class", deprecated:"false" },
+      { id:111, label:"com.google.android.gms.maps.GoogleMap.InfoWindowAdapter", link:"reference/com/google/android/gms/maps/GoogleMap.InfoWindowAdapter.html", type:"class", deprecated:"false" },
+      { id:112, label:"com.google.android.gms.maps.GoogleMap.OnCameraChangeListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnCameraChangeListener.html", type:"class", deprecated:"false" },
+      { id:113, label:"com.google.android.gms.maps.GoogleMap.OnInfoWindowClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnInfoWindowClickListener.html", type:"class", deprecated:"false" },
+      { id:114, label:"com.google.android.gms.maps.GoogleMap.OnMapClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMapClickListener.html", type:"class", deprecated:"false" },
+      { id:115, label:"com.google.android.gms.maps.GoogleMap.OnMapLongClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMapLongClickListener.html", type:"class", deprecated:"false" },
+      { id:116, label:"com.google.android.gms.maps.GoogleMap.OnMarkerClickListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMarkerClickListener.html", type:"class", deprecated:"false" },
+      { id:117, label:"com.google.android.gms.maps.GoogleMap.OnMarkerDragListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMarkerDragListener.html", type:"class", deprecated:"false" },
+      { id:118, label:"com.google.android.gms.maps.GoogleMap.OnMyLocationChangeListener", link:"reference/com/google/android/gms/maps/GoogleMap.OnMyLocationChangeListener.html", type:"class", deprecated:"true" },
+      { id:119, label:"com.google.android.gms.maps.GoogleMapOptions", link:"reference/com/google/android/gms/maps/GoogleMapOptions.html", type:"class", deprecated:"false" },
+      { id:120, label:"com.google.android.gms.maps.LocationSource", link:"reference/com/google/android/gms/maps/LocationSource.html", type:"class", deprecated:"false" },
+      { id:121, label:"com.google.android.gms.maps.LocationSource.OnLocationChangedListener", link:"reference/com/google/android/gms/maps/LocationSource.OnLocationChangedListener.html", type:"class", deprecated:"false" },
+      { id:122, label:"com.google.android.gms.maps.MapFragment", link:"reference/com/google/android/gms/maps/MapFragment.html", type:"class", deprecated:"false" },
+      { id:123, label:"com.google.android.gms.maps.MapView", link:"reference/com/google/android/gms/maps/MapView.html", type:"class", deprecated:"false" },
+      { id:124, label:"com.google.android.gms.maps.MapsInitializer", link:"reference/com/google/android/gms/maps/MapsInitializer.html", type:"class", deprecated:"false" },
+      { id:125, label:"com.google.android.gms.maps.Projection", link:"reference/com/google/android/gms/maps/Projection.html", type:"class", deprecated:"false" },
+      { id:126, label:"com.google.android.gms.maps.SupportMapFragment", link:"reference/com/google/android/gms/maps/SupportMapFragment.html", type:"class", deprecated:"false" },
+      { id:127, label:"com.google.android.gms.maps.UiSettings", link:"reference/com/google/android/gms/maps/UiSettings.html", type:"class", deprecated:"false" },
+      { id:128, label:"com.google.android.gms.maps.model", link:"reference/com/google/android/gms/maps/model/package-summary.html", type:"package", deprecated:"false" },
+      { id:129, label:"com.google.android.gms.maps.model.BitmapDescriptor", link:"reference/com/google/android/gms/maps/model/BitmapDescriptor.html", type:"class", deprecated:"false" },
+      { id:130, label:"com.google.android.gms.maps.model.BitmapDescriptorFactory", link:"reference/com/google/android/gms/maps/model/BitmapDescriptorFactory.html", type:"class", deprecated:"false" },
+      { id:131, label:"com.google.android.gms.maps.model.CameraPosition", link:"reference/com/google/android/gms/maps/model/CameraPosition.html", type:"class", deprecated:"false" },
+      { id:132, label:"com.google.android.gms.maps.model.CameraPosition.Builder", link:"reference/com/google/android/gms/maps/model/CameraPosition.Builder.html", type:"class", deprecated:"false" },
+      { id:133, label:"com.google.android.gms.maps.model.Circle", link:"reference/com/google/android/gms/maps/model/Circle.html", type:"class", deprecated:"false" },
+      { id:134, label:"com.google.android.gms.maps.model.CircleOptions", link:"reference/com/google/android/gms/maps/model/CircleOptions.html", type:"class", deprecated:"false" },
+      { id:135, label:"com.google.android.gms.maps.model.GroundOverlay", link:"reference/com/google/android/gms/maps/model/GroundOverlay.html", type:"class", deprecated:"false" },
+      { id:136, label:"com.google.android.gms.maps.model.GroundOverlayOptions", link:"reference/com/google/android/gms/maps/model/GroundOverlayOptions.html", type:"class", deprecated:"false" },
+      { id:137, label:"com.google.android.gms.maps.model.LatLng", link:"reference/com/google/android/gms/maps/model/LatLng.html", type:"class", deprecated:"false" },
+      { id:138, label:"com.google.android.gms.maps.model.LatLngBounds", link:"reference/com/google/android/gms/maps/model/LatLngBounds.html", type:"class", deprecated:"false" },
+      { id:139, label:"com.google.android.gms.maps.model.LatLngBounds.Builder", link:"reference/com/google/android/gms/maps/model/LatLngBounds.Builder.html", type:"class", deprecated:"false" },
+      { id:140, label:"com.google.android.gms.maps.model.Marker", link:"reference/com/google/android/gms/maps/model/Marker.html", type:"class", deprecated:"false" },
+      { id:141, label:"com.google.android.gms.maps.model.MarkerOptions", link:"reference/com/google/android/gms/maps/model/MarkerOptions.html", type:"class", deprecated:"false" },
+      { id:142, label:"com.google.android.gms.maps.model.Polygon", link:"reference/com/google/android/gms/maps/model/Polygon.html", type:"class", deprecated:"false" },
+      { id:143, label:"com.google.android.gms.maps.model.PolygonOptions", link:"reference/com/google/android/gms/maps/model/PolygonOptions.html", type:"class", deprecated:"false" },
+      { id:144, label:"com.google.android.gms.maps.model.Polyline", link:"reference/com/google/android/gms/maps/model/Polyline.html", type:"class", deprecated:"false" },
+      { id:145, label:"com.google.android.gms.maps.model.PolylineOptions", link:"reference/com/google/android/gms/maps/model/PolylineOptions.html", type:"class", deprecated:"false" },
+      { id:146, label:"com.google.android.gms.maps.model.RuntimeRemoteException", link:"reference/com/google/android/gms/maps/model/RuntimeRemoteException.html", type:"class", deprecated:"false" },
+      { id:147, label:"com.google.android.gms.maps.model.Tile", link:"reference/com/google/android/gms/maps/model/Tile.html", type:"class", deprecated:"false" },
+      { id:148, label:"com.google.android.gms.maps.model.TileOverlay", link:"reference/com/google/android/gms/maps/model/TileOverlay.html", type:"class", deprecated:"false" },
+      { id:149, label:"com.google.android.gms.maps.model.TileOverlayOptions", link:"reference/com/google/android/gms/maps/model/TileOverlayOptions.html", type:"class", deprecated:"false" },
+      { id:150, label:"com.google.android.gms.maps.model.TileProvider", link:"reference/com/google/android/gms/maps/model/TileProvider.html", type:"class", deprecated:"false" },
+      { id:151, label:"com.google.android.gms.maps.model.UrlTileProvider", link:"reference/com/google/android/gms/maps/model/UrlTileProvider.html", type:"class", deprecated:"false" },
+      { id:152, label:"com.google.android.gms.maps.model.VisibleRegion", link:"reference/com/google/android/gms/maps/model/VisibleRegion.html", type:"class", deprecated:"false" },
+      { id:153, label:"com.google.android.gms.panorama", link:"reference/com/google/android/gms/panorama/package-summary.html", type:"package", deprecated:"false" },
+      { id:154, label:"com.google.android.gms.panorama.PanoramaClient", link:"reference/com/google/android/gms/panorama/PanoramaClient.html", type:"class", deprecated:"false" },
+      { id:155, label:"com.google.android.gms.panorama.PanoramaClient.OnPanoramaInfoLoadedListener", link:"reference/com/google/android/gms/panorama/PanoramaClient.OnPanoramaInfoLoadedListener.html", type:"class", deprecated:"false" },
+      { id:156, label:"com.google.android.gms.plus", link:"reference/com/google/android/gms/plus/package-summary.html", type:"package", deprecated:"false" },
+      { id:157, label:"com.google.android.gms.plus.GooglePlusUtil", link:"reference/com/google/android/gms/plus/GooglePlusUtil.html", type:"class", deprecated:"false" },
+      { id:158, label:"com.google.android.gms.plus.PlusClient", link:"reference/com/google/android/gms/plus/PlusClient.html", type:"class", deprecated:"false" },
+      { id:159, label:"com.google.android.gms.plus.PlusClient.Builder", link:"reference/com/google/android/gms/plus/PlusClient.Builder.html", type:"class", deprecated:"false" },
+      { id:160, label:"com.google.android.gms.plus.PlusClient.OnAccessRevokedListener", link:"reference/com/google/android/gms/plus/PlusClient.OnAccessRevokedListener.html", type:"class", deprecated:"false" },
+      { id:161, label:"com.google.android.gms.plus.PlusClient.OnMomentsLoadedListener", link:"reference/com/google/android/gms/plus/PlusClient.OnMomentsLoadedListener.html", type:"class", deprecated:"false" },
+      { id:162, label:"com.google.android.gms.plus.PlusClient.OnPeopleLoadedListener", link:"reference/com/google/android/gms/plus/PlusClient.OnPeopleLoadedListener.html", type:"class", deprecated:"false" },
+      { id:163, label:"com.google.android.gms.plus.PlusClient.OnPersonLoadedListener", link:"reference/com/google/android/gms/plus/PlusClient.OnPersonLoadedListener.html", type:"class", deprecated:"false" },
+      { id:164, label:"com.google.android.gms.plus.PlusOneButton", link:"reference/com/google/android/gms/plus/PlusOneButton.html", type:"class", deprecated:"false" },
+      { id:165, label:"com.google.android.gms.plus.PlusOneButton.OnPlusOneClickListener", link:"reference/com/google/android/gms/plus/PlusOneButton.OnPlusOneClickListener.html", type:"class", deprecated:"false" },
+      { id:166, label:"com.google.android.gms.plus.PlusShare", link:"reference/com/google/android/gms/plus/PlusShare.html", type:"class", deprecated:"false" },
+      { id:167, label:"com.google.android.gms.plus.PlusShare.Builder", link:"reference/com/google/android/gms/plus/PlusShare.Builder.html", type:"class", deprecated:"false" },
+      { id:168, label:"com.google.android.gms.plus.model.moments", link:"reference/com/google/android/gms/plus/model/moments/package-summary.html", type:"package", deprecated:"false" },
+      { id:169, label:"com.google.android.gms.plus.model.moments.ItemScope", link:"reference/com/google/android/gms/plus/model/moments/ItemScope.html", type:"class", deprecated:"false" },
+      { id:170, label:"com.google.android.gms.plus.model.moments.ItemScope.Builder", link:"reference/com/google/android/gms/plus/model/moments/ItemScope.Builder.html", type:"class", deprecated:"false" },
+      { id:171, label:"com.google.android.gms.plus.model.moments.Moment", link:"reference/com/google/android/gms/plus/model/moments/Moment.html", type:"class", deprecated:"false" },
+      { id:172, label:"com.google.android.gms.plus.model.moments.Moment.Builder", link:"reference/com/google/android/gms/plus/model/moments/Moment.Builder.html", type:"class", deprecated:"false" },
+      { id:173, label:"com.google.android.gms.plus.model.moments.MomentBuffer", link:"reference/com/google/android/gms/plus/model/moments/MomentBuffer.html", type:"class", deprecated:"false" },
+      { id:174, label:"com.google.android.gms.plus.model.people", link:"reference/com/google/android/gms/plus/model/people/package-summary.html", type:"package", deprecated:"false" },
+      { id:175, label:"com.google.android.gms.plus.model.people.Person", link:"reference/com/google/android/gms/plus/model/people/Person.html", type:"class", deprecated:"false" },
+      { id:176, label:"com.google.android.gms.plus.model.people.Person.AgeRange", link:"reference/com/google/android/gms/plus/model/people/Person.AgeRange.html", type:"class", deprecated:"false" },
+      { id:177, label:"com.google.android.gms.plus.model.people.Person.Collection", link:"reference/com/google/android/gms/plus/model/people/Person.Collection.html", type:"class", deprecated:"false" },
+      { id:178, label:"com.google.android.gms.plus.model.people.Person.Cover", link:"reference/com/google/android/gms/plus/model/people/Person.Cover.html", type:"class", deprecated:"false" },
+      { id:179, label:"com.google.android.gms.plus.model.people.Person.Cover.CoverInfo", link:"reference/com/google/android/gms/plus/model/people/Person.Cover.CoverInfo.html", type:"class", deprecated:"false" },
+      { id:180, label:"com.google.android.gms.plus.model.people.Person.Cover.CoverPhoto", link:"reference/com/google/android/gms/plus/model/people/Person.Cover.CoverPhoto.html", type:"class", deprecated:"false" },
+      { id:181, label:"com.google.android.gms.plus.model.people.Person.Cover.Layout", link:"reference/com/google/android/gms/plus/model/people/Person.Cover.Layout.html", type:"class", deprecated:"false" },
+      { id:182, label:"com.google.android.gms.plus.model.people.Person.Emails", link:"reference/com/google/android/gms/plus/model/people/Person.Emails.html", type:"class", deprecated:"false" },
+      { id:183, label:"com.google.android.gms.plus.model.people.Person.Emails.Type", link:"reference/com/google/android/gms/plus/model/people/Person.Emails.Type.html", type:"class", deprecated:"false" },
+      { id:184, label:"com.google.android.gms.plus.model.people.Person.Gender", link:"reference/com/google/android/gms/plus/model/people/Person.Gender.html", type:"class", deprecated:"false" },
+      { id:185, label:"com.google.android.gms.plus.model.people.Person.Image", link:"reference/com/google/android/gms/plus/model/people/Person.Image.html", type:"class", deprecated:"false" },
+      { id:186, label:"com.google.android.gms.plus.model.people.Person.Name", link:"reference/com/google/android/gms/plus/model/people/Person.Name.html", type:"class", deprecated:"false" },
+      { id:187, label:"com.google.android.gms.plus.model.people.Person.ObjectType", link:"reference/com/google/android/gms/plus/model/people/Person.ObjectType.html", type:"class", deprecated:"false" },
+      { id:188, label:"com.google.android.gms.plus.model.people.Person.OrderBy", link:"reference/com/google/android/gms/plus/model/people/Person.OrderBy.html", type:"class", deprecated:"false" },
+      { id:189, label:"com.google.android.gms.plus.model.people.Person.Organizations", link:"reference/com/google/android/gms/plus/model/people/Person.Organizations.html", type:"class", deprecated:"false" },
+      { id:190, label:"com.google.android.gms.plus.model.people.Person.Organizations.Type", link:"reference/com/google/android/gms/plus/model/people/Person.Organizations.Type.html", type:"class", deprecated:"false" },
+      { id:191, label:"com.google.android.gms.plus.model.people.Person.PlacesLived", link:"reference/com/google/android/gms/plus/model/people/Person.PlacesLived.html", type:"class", deprecated:"false" },
+      { id:192, label:"com.google.android.gms.plus.model.people.Person.RelationshipStatus", link:"reference/com/google/android/gms/plus/model/people/Person.RelationshipStatus.html", type:"class", deprecated:"false" },
+      { id:193, label:"com.google.android.gms.plus.model.people.Person.Urls", link:"reference/com/google/android/gms/plus/model/people/Person.Urls.html", type:"class", deprecated:"false" },
+      { id:194, label:"com.google.android.gms.plus.model.people.Person.Urls.Type", link:"reference/com/google/android/gms/plus/model/people/Person.Urls.Type.html", type:"class", deprecated:"false" },
+      { id:195, label:"com.google.android.gms.plus.model.people.PersonBuffer", link:"reference/com/google/android/gms/plus/model/people/PersonBuffer.html", type:"class", deprecated:"false" }
 
     ];
diff --git a/docs/html/sdk/index.jd b/docs/html/sdk/index.jd
index 0d0f348..aa3b2ec 100644
--- a/docs/html/sdk/index.jd
+++ b/docs/html/sdk/index.jd
@@ -5,43 +5,43 @@
 page.metaDescription=Download the official Android SDK to develop apps for Android-powered devices.
 
 
-sdk.linux32_bundle_download=adt-bundle-linux-x86-20130522.zip
-sdk.linux32_bundle_bytes=439988972
-sdk.linux32_bundle_checksum=1fdd8d7537ab9217d61d32ab912f0243
+sdk.linux32_bundle_download=adt-bundle-linux-x86-20130717.zip
+sdk.linux32_bundle_bytes=440035305
+sdk.linux32_bundle_checksum=ecfacb91df1ee63cce1edd4f1a5cda5a
 
-sdk.linux64_bundle_download=adt-bundle-linux-x86_64-20130522.zip
-sdk.linux64_bundle_bytes=440275051
-sdk.linux64_bundle_checksum=e38751ff372a8d6208fcef5659133e98
+sdk.linux64_bundle_download=adt-bundle-linux-x86_64-20130717.zip
+sdk.linux64_bundle_bytes=440322117
+sdk.linux64_bundle_checksum=ab177a06784340b8f1d136651e3dc62a
 
-sdk.mac64_bundle_download=adt-bundle-mac-x86_64-20130522.zip
-sdk.mac64_bundle_bytes=409047751
-sdk.mac64_bundle_checksum=3f4d05206d66e402e87b27a6b3dcf0f9
+sdk.mac64_bundle_download=adt-bundle-mac-x86_64-20130717.zip
+sdk.mac64_bundle_bytes=411609229
+sdk.mac64_bundle_checksum=07c891212a49b5f8495ea9d8d47ba3fe
 
-sdk.win32_bundle_download=adt-bundle-windows-x86-20130522.zip
-sdk.win32_bundle_bytes=446736316
-sdk.win32_bundle_checksum=53345fa4121fa58cc048f66c3af3bae9
+sdk.win32_bundle_download=adt-bundle-windows-x86-20130717.zip
+sdk.win32_bundle_bytes=446783216
+sdk.win32_bundle_checksum=0dd91095999d3539ca1ec4033d83d935
 
-sdk.win64_bundle_download=adt-bundle-windows-x86_64-20130522.zip
-sdk.win64_bundle_bytes=446864400
-sdk.win64_bundle_checksum=b28817f62e7f54e3c683841b61b65564
+sdk.win64_bundle_download=adt-bundle-windows-x86_64-20130717.zip
+sdk.win64_bundle_bytes=446911629
+sdk.win64_bundle_checksum=61ec74995b39166db7f079017a028cec
 
 
 
-sdk.linux_download=android-sdk_r22.0.1-linux.tgz
-sdk.linux_bytes=105617062
-sdk.linux_checksum=56ed27d456b4f0e0d3090b24f9b06757
+sdk.linux_download=android-sdk_r22.0.4-linux.tgz
+sdk.linux_bytes=105640988
+sdk.linux_checksum=4a5db98a58c68c24e66f04f07ac77da5
 
-sdk.mac_download=android-sdk_r22.0.1-macosx.zip
-sdk.mac_bytes=77206237
-sdk.mac_checksum=5c20497d7f7b9d28ee30e57cbf769c8c
+sdk.mac_download=android-sdk_r22.0.4-macosx.zip
+sdk.mac_bytes=77225662
+sdk.mac_checksum=384752505f4f2ba3627bd6aad0697f11
 
-sdk.win_download=android-sdk_r22.0.1-windows.zip
-sdk.win_bytes=113483496
-sdk.win_checksum=cb7f7703450afa5914fb4b8b8332a9f3
+sdk.win_download=android-sdk_r22.0.4-windows.zip
+sdk.win_bytes=113507679
+sdk.win_checksum=320b11d1ed85fd3f5e937697c333d895
 
-sdk.win_installer=installer_r22.0.1-windows.exe
-sdk.win_installer_bytes=93479015
-sdk.win_installer_checksum=81621d3b164f81f91e066011b325f88f
+sdk.win_installer=installer_r22.0.4-windows.exe
+sdk.win_installer_bytes=93502726
+sdk.win_installer_checksum=96a8ae367d84ed219e1eb2cf473667d0
 
 
 
diff --git a/docs/html/sdk/installing/installing-adt.jd b/docs/html/sdk/installing/installing-adt.jd
index b8ab24b..2a09636 100644
--- a/docs/html/sdk/installing/installing-adt.jd
+++ b/docs/html/sdk/installing/installing-adt.jd
@@ -1,8 +1,8 @@
 page.title=Installing the Eclipse Plugin
-adt.zip.version=22.0.1
-adt.zip.download=ADT-22.0.1.zip
-adt.zip.bytes=16815544
-adt.zip.checksum=64473af058fa8f02e36241ee378b3ac0
+adt.zip.version=22.0.4
+adt.zip.download=ADT-22.0.4.zip
+adt.zip.bytes=16838756
+adt.zip.checksum=f0291f4bb9d78ec34a7751cd2402cc2a
 
 @jd:body
 
diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd
index 8825bcc..9a29599 100644
--- a/docs/html/sdk/installing/studio.jd
+++ b/docs/html/sdk/installing/studio.jd
@@ -249,36 +249,36 @@
     <td>Windows</td>
     <td>
   <a onclick="return onDownload(this)" id="win-studio"
-      href="http://dl.google.com/android/studio/android-studio-bundle-130.687321-windows.exe">
-      android-studio-bundle-130.677228-windows.exe
+      href="http://dl.google.com/android/studio/android-studio-bundle-130.737825-windows.exe">
+      android-studio-bundle-130.737825-windows.exe
       </a>
     </td>
-    <td>393023485 bytes</td>
-    <td>3da987a9778b66edb68fb43d8b53bfcb</td>
+    <td>396091268 bytes</td>
+    <td>6da1bc8effa048c8ff669e4c484eb11f</td>
   </tr>
 
   <tr>
     <td><nobr>Mac OS X</nobr></td>
     <td>
   <a onclick="return onDownload(this)" id="mac-studio"
-    href="http://dl.google.com/android/studio/android-studio-bundle-130.687321-mac.dmg">
-    android-studio-bundle-130.687321-mac.dmg
+    href="http://dl.google.com/android/studio/android-studio-bundle-130.737825-mac.dmg">
+    android-studio-bundle-130.737825-mac.dmg
     </a>
     </td>
-    <td>379877697 bytes</td>
-    <td>eb5ca6c77f4a119595d941daeda58810</td>
+    <td>383326582 bytes</td>
+    <td>2959bc5039238d286670cc6225342b89</td>
   </tr>
 
   <tr>
     <td>Linux</td>
     <td>
   <a onclick="return onDownload(this)" id="linux-studio"
-    href="http://dl.google.com/android/studio/android-studio-bundle-130.687321-linux.tgz">
-    android-studio-bundle-130.687321-linux.tgz
+    href="http://dl.google.com/android/studio/android-studio-bundle-130.737825-linux.tgz">
+    android-studio-bundle-130.737825-linux.tgz
     </a>
     </td>
-    <td>406516375 bytes</td>
-    <td>6796d66de07c85b2822ca8d501a043c0</td>
+    <td>409935592 bytes</td>
+    <td>dcd13922f7cf577e3c852b224205d843</td>
   </tr>
   </table>
 
@@ -286,6 +286,31 @@
 
 
 
+<h2 id="Updating">Updating to 0.2.x</h2>
+
+<p>To update from Android Studio 0.1.x to 0.2.x,
+follow the <a href="#Installing">installation instructions</a> below and replace your existing
+installation.</p>
+
+<div class="caution">
+<p><strong>Caution:</strong> Replacing your existing installation of
+Android Studio will remove any additional SDK packages you've installed, such as target
+platforms, system images, and sample apps. To preserve these, copy them from your current
+SDK directory under Android Studio to a temporary location
+before installing the update. Then move them back once the update is complete.
+If you fail to copy these packages, then you can instead download them again through
+the Android SDK Manager.</p>
+<p><strong>Windows users:</strong> Do not install Android Studio 0.2.x in the same
+location as 0.1.x. Doing so may cause errors such as ClassCastException or other unexpected
+behaviors. It's best if you remove your previous version of Android Studio 0.1.x.</p>
+</div>
+
+
+<p>Also note that due to the update to Gradle 0.5, you will encounter errors when opening
+existing projects. See the <a href="#Troubleshooting">Troubleshooting</a> notes below for
+information about how to resolve them.</p>
+
+
 <h2 id="Installing">Installing Android Studio</h2>
 <ol>
 <li>Download the <strong>Android Studio</strong> package from above.</li>
@@ -390,14 +415,78 @@
 
 <h2 id="Revisions">Revisions</h2>
 
-<p class="note"><strong>Note:</strong> Periodic updates are pushed to Android Studio
-without requiring you to update from here. To manually check for updates, select
-<strong>Help > Check for updates</strong> (on Mac, select <strong>Android Studio >
-Check for updates</strong>).</p>
+<p class="note"><strong>Note:</strong> <strong>There is not a patch update available from
+0.1.9 to 0.2</strong>. To update from Android Studio 0.1.x to 0.2.x, you must
+install a new Android Studio bundle from this page.  The reason for that is that we have made
+changes to the bundled SDK such that it includes a pre-configured local Maven repository
+which can serve up the v4 support library and which is required for creating new projects.</p>
+
 
 <div class="toggle-content opened">
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+      alt=""/>Android Studio v0.2.x</a> <em>(July 2013)</em>
+  </p>
+
+  <div class="toggle-content-toggleme">
+  <ul>
+    <li>Merged in the latest IntelliJ codebase changes. Includes fixes for issues reported by Studio users such as tweaks to Linux font sizes and font rendering.</li>
+    <li>Android Gradle plug-in updated to 0.5.0.
+      <p class="caution"><strong>Caution:</strong> This new version is not backwards compatible.
+      When opening a project that uses an older version of the plug-in, Studio will show an error
+      stating <strong>Gradle &lt;project_name&gt; project refresh failed.</strong> See <a
+      href="#Troubleshooting">Troubleshooting</a> below for details.</p>
+      <p>The updated Gradle plug-in includes the following changes:</p>
+      <ul>
+        <li>Fixed IDE model to contain the output file even if it's customized through the DSL. Also
+        fixed the DSL to get/set the output file on the variant object so that it's not necessary to
+        use <code>variant.packageApplication or variant.zipAlign</code></li>
+        <li>Fixed dependency resolution so that we resolved the combination of (default config,
+        build types, flavor(s)) together instead of separately.</li>
+        <li>Fixed dependency for tests of library project to properly include all the dependencies
+        of the library itself.</li>
+        <li>Fixed case where two dependencies have the same leaf name.</li>
+        <li>Fixed issue where Proguard rules file cannot be applied on flavors.</li>
+      </ul>
+      <p>All Gradle plugin release notes are available are here: <a href=
+      "http://tools.android.com/tech-docs/new-build-system"
+      >http://tools.android.com/tech-docs/new-build-system</a>.</p>
+    </li>
+    <li>Gradle errors from aapt no longer point to merged output files in the build/ folder, they
+    point back to the real source locations.</li>
+    <li>Parallel Builds. It's now possible to use Gradle's parallel builds. Please be aware that
+    parallel builds are in "incubation" (see <a
+    href="http://www.gradle.org/docs/current/userguide/gradle_command_line.html">Gradle's
+    documentation</a>.) This feature is off by default. To enable it, go to
+    <strong>Preferences</strong> &gt; <strong>Compiler</strong> and check the box <em>Compile
+    independent modules in parallel</em>.</li>
+    <li>Further work on the new resource repository used for layout rendering, resource
+    folding in the editor, and more:
+      <ul>
+      <li>Basic support for .aar library dependencies (e.g. using a library without a local copy of
+      the sources). Still not working for resource XML validation and navigation in source editors.
+      </li>
+      <li>Cycle detection in resource references.</li>
+      <li>Quick Documentation (F1), which can show all translations of the string under the caret,
+      will now also show all resource overlays from the various Gradle flavors and build types, as
+      well as libraries. They are listed in reverse resource overlay order, with strikethrough on
+      the versions of the string that are masked.</li>
+      <li>Fixes to handle updating the merged resources when the set of module dependencies
+      change.</li>
+      <li>XML rendering fixes to properly handle character entity declarations and XML and unicode
+      escapes.</li>
+      </ul>
+    <li>Save screenshot support for the layout preview and layout editor windows.</li>
+    <li>Template bug fixes.</li>
+    <li>Lint bug fixes.</li>
+    <li>Various fixes for crash reports. Thank you, and keep filing crash reports!</li>
+  </ul>
+  </div>
+</div>
+
+<div class="toggle-content closed">
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
       alt=""/>Android Studio v0.1.x</a> <em>(May 2013)</em>
   </p>
 
@@ -408,12 +497,79 @@
   </div>
 </div>
 
+<p>&nbsp;</p>
+
+<p class="note"><strong>Note:</strong> Periodic updates are pushed to Android Studio
+without requiring you to update from here. To manually check for updates, select
+<strong>Help > Check for updates</strong> (on Mac, select <strong>Android Studio >
+Check for updates</strong>).</p>
 
 
 
 
+<h2 id="Troubleshooting">Troubleshooting</h2>
 
 
+<div class="figure" style="width:330px">
+<img src="{@docRoot}images/tools/studio_error_gradle5.png" width="330"/>
+<p class="img-caption"><strong>Figure 1.</strong> Error dialog when opening an existing project.</p>
+</div>
+
+<h3>Error: Gradle project refresh failed</h3>
+
+<p>Android Studio 0.2.0 has updated the Gradle plug-in to 0.5.0, which is not backwards compatible.
+When opening a project that uses an older version of the plug-in, Studio will display the error
+shown in figure 1 in the upper right corner of the IDE.
+To resolve the error, you must change the version of the Android Gradle plug-in to 0.5.0.</p>
+
+<ol>
+  <li>Click the link in the error dialog <strong>Search in build.gradle files</strong>. If the dialog
+is no longer visible, click <strong>Event Log</strong>
+<img src="{@docRoot}images/tools/studio_error_eventlog.png"
+style="vertical-align:bottom;margin:0;height:19px"/> in the bottom-right corner of the IDE,
+then click <strong>Search in build.gradle files</strong>.</li>
+  <li>Double-click the line under the <em>build.gradle</em> usage. For example:
+  <strong>classpath 'com.android.tools.build:gradle:0.4</strong>. This opens the project
+  <code>build.gradle</code> file.</li>
+  <li>Edit the <code>classpath</code> to change the gradle version to <code>0.5.+</code>.
+  For example:
+  <pre class="no-pretty-print">
+dependencies {
+  classpath 'com.android.tools.build:gradle:<strong>0.5.+</strong>'
+}
+</pre>
+  </li>
+  <li>Save the file and rebuild your project.</li>
+</ol>
+
+
+
+<div class="figure" style="width:330px">
+<img src="{@docRoot}images/tools/studio_error_supportlib.png" width="330"/>
+<p class="img-caption"><strong>Figure 2.</strong> Error dialog when creating a new project
+or opening a project using the support library.</p>
+</div>
+
+<h3>Error: Failed to import Gradle project</h3>
+
+<p>If, after updating to Android Studio 0.2.x and creating or opening a project, you receive an
+error stating <em>"Could not find any version that matches
+com.android.support:support-v4:13.0.+"</em>, then you must install the <strong>Android Support
+Repository</strong>. This was likely caused because you're pointing Android Studio to an external
+Android SDK location that does not have the new Maven repository included with Android Studio
+0.2.x. This new Maven repository is used by the new build system for the Support Library, instead
+of using the Support Library JAR files, so must be present in the SDK.</p>
+
+
+<ol>
+  <li>Open the <strong>Android SDK Manager</strong>.</li>
+  <li>Expand the <strong>Extras</strong> directory
+and install <strong>Android Support Repository</strong>.</li>
+</ol>
+
+<p>If you've encountered other problems in Android Studio, look at the following page
+for possible resolutions to known issues: <a href="http://tools.android.com/knownissues"
+>http://tools.android.com/knownissues</a>.</p>
 
 
 
@@ -430,7 +586,7 @@
 
 
 
-  
+
 <script>
   var os;
   var bundlename;
@@ -450,15 +606,15 @@
   if (os) {
     /* set up primary ACE download button */
     $('#download-ide-button').show();
-    $('#download-ide-button').append("Download Android Studio <span class='small'>v0.1.x</span>"
-        + "<br/><span class='small'>for " + os + "</span>");
+    $('#download-ide-button').append("Download Android Studio <span class='small'>v0.2.x</span>"
+        + "<br/> <span class='small'>for " + os + "</span>");
     $('#download-ide-button').click(function() {return onDownload(this,true);}).attr('href', bundlename);
 
   } else {
     $('.pax').show();
   }
-  
-  
+
+
   function onDownload(link, button) {
     var $studioLink;
 
@@ -470,7 +626,7 @@
       $studioLink = $(link);
       $("#downloadForRealz").html("Download " + $(link).text());
     }
-    
+
     $("#downloadForRealz").attr('href', $studioLink.attr('href'));
 
     $("#tos").fadeIn('fast');
@@ -493,10 +649,9 @@
 
   function onDownloadForRealz(link) {
     if ($("input#agree").is(':checked')) {
-      $("div.sdk-terms,#sdk-terms-form,.sdk-terms-intro").hide();
-      $("#main").show(function() {
-          location.hash = "Installing";
-          });
+      $("#tos").hide();
+      $("#main").show();
+      location.hash = "Updating";
       return true;
     } else {
       $("label#agreeLabel,#bitpicker input").parent().stop().animate({color: "#258AAF"}, 200,
diff --git a/docs/html/tools/extras/oem-usb.jd b/docs/html/tools/extras/oem-usb.jd
index 87734a1..5e0e893 100644
--- a/docs/html/tools/extras/oem-usb.jd
+++ b/docs/html/tools/extras/oem-usb.jd
@@ -250,14 +250,6 @@
 href="http://www.fmworld.net/product/phone/sp/android/develop/">http://www.fmworld.net/product/phone/sp/android/develop/</a>
     </td>
   </tr>
-  <tr>
-    <td>
-      Fujitsu Toshiba
-    </td>
-    <td><a
-href="http://www.fmworld.net/product/phone/sp/android/develop/">http://www.fmworld.net/product/phone/sp/android/develop/</a>
-    </td>
-  </tr>
   <tr><td>
        Garmin-Asus
     </td>	<td><a
@@ -327,6 +319,10 @@
 href="http://www.teleepoch.com/android.html">http://www.teleepoch.com/android.html</a></td>
 </tr>
 
+<tr><td>Toshiba</td>  <td><a
+href="http://support.toshiba.com/sscontent?docId=4001814">http://support.toshiba.com/sscontent?docId=4001814</a></td>
+</tr>
+
 <tr><td>Yulong Coolpad</td>	<td><a
 href="http://www.yulong.com/product/product/product/downloadList.html#downListUL">http://www.yulong.com/product/product/product/downloadList.html#downListUL</a></td>
 </tr>
diff --git a/docs/html/tools/index.jd b/docs/html/tools/index.jd
index f9d452c..e9094a7 100644
--- a/docs/html/tools/index.jd
+++ b/docs/html/tools/index.jd
@@ -1,6 +1,13 @@
 page.title=Developer Tools
 @jd:body
-
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
     
 <img src="{@docRoot}images/tools-home.png" style="float:right;" height="415" width="763" />
 
diff --git a/docs/html/tools/revisions/platforms.jd b/docs/html/tools/revisions/platforms.jd
index 31cec0e..820edbd 100644
--- a/docs/html/tools/revisions/platforms.jd
+++ b/docs/html/tools/revisions/platforms.jd
@@ -29,25 +29,46 @@
 version.</p>
 
 <p>To determine what revision of an Android platform you have installed, refer to the
-<strong>Installed Packages</strong> listing in the Android SDK Manager.</p>
+<strong>Installed Packages</strong> listing in the Android
+<a href="{@docRoot}tools/help/sdk-manager.html">SDK Manager</a>.</p>
+
+<p class="caution"><strong>Important:</strong> To download the most recent Android
+system components from the Android SDK Manager, you must first update the SDK Tools to
+revision 22 or later and restart the SDK Manager. If you do not,
+the latest Android system components will not be available for download.</p>
 
 
 
+<h2 id="4.3">Android 4.3</h2>
 
 
-
-<h2 id="4.2">Android 4.2</h2>
-
-
-<p class="caution"><strong>Important:</strong> To download the new Android
-4.2.x system components from the Android SDK Manager, you must first update the
-SDK tools to revision 20 or later and restart the Android SDK Manager. If you do not,
-the Android 4.2 system components will not be available for download.</p>
-
 <div class="toggle-content opened">
 
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png"
+class="toggle-content-img" alt="" />Revision 1</a> <em>(July 2013)</em>
+  </p>
+
+  <div class="toggle-content-toggleme">
+
+    <p>Initial release. The system version is 4.3.</p>
+    <dl>
+      <dt>Dependencies:</dt>
+      <dd>Android SDK Platform-tools r18 or higher is required.</dd>
+      <dd>Android SDK Tools 22.0.4 or higher is recommended.</dd>
+    </dl>
+
+  </div>
+</div>
+
+
+<h2 id="4.2">Android 4.2</h2>
+
+
+<div class="toggle-content closed">
+
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png"
 class="toggle-content-img" alt="" />Revision 2</a> <em>(February 2013)</em>
   </p>
 
@@ -150,12 +171,6 @@
 <h2 id="4.1">Android 4.1</h2>
 
 
-<p class="caution"><strong>Important:</strong> To download the new Android
-4.1.x system components from the Android SDK Manager, you must first update the
-SDK tools to revision 20 or later and restart the Android SDK Manager. If you do not,
-the Android 4.1 system components will not be available for download.</p>
-
-
 <div class="toggle-content closed">
 
   <p><a href="#" onclick="return toggleContent(this)">
@@ -269,11 +284,6 @@
 <h2 id="4.0.3">Android 4.0.3</h2>
 
 
-<p class="caution"><strong>Important:</strong> To download the new Android
-4.0.x system components from the Android SDK Manager, you must first update the
-SDK tools to revision 14 or later and restart the Android SDK Manager. If you do not,
-the Android 4.0.x system components will not be available for download.</p>
-
 <div class="toggle-content closed">
 
   <p><a href="#" onclick="return toggleContent(this)">
@@ -711,13 +721,6 @@
 <h2 id="2.3.4">Android 2.3.4</h2>
 
 
-
-<p>The sections below provide notes about successive releases of
-the Android 2.3.4 platform component for the Android SDK, as denoted by
-revision number. To determine what revision(s) of the Android
-2.3.4 platforms are installed in your SDK environment, refer to
-the "Installed Packages" listing in the Android SDK and AVD Manager.</p>
-
 <div class="toggle-content closed" >
 
 <p><a href="#" onclick="return toggleContent(this)">
@@ -865,14 +868,6 @@
 <h2 id="2.3">Android 2.3</h2>
 
 
-
-<p>The sections below provide notes about successive releases of
-the Android 2.3 platform component for the Android SDK, as denoted by
-revision number. To determine what revision(s) of the Android
-2.3 platforms are installed in your SDK environment, refer to
-the "Installed Packages" listing in the Android SDK and AVD Manager.</p>
-
-
 <div class="toggle-content closed" >
 
 <p><a href="#" onclick="return toggleContent(this)">
@@ -1033,4 +1028,4 @@
   <li>
     WVGA854 (480x854 high density, normal screen)
   </li>
-</ul>
\ No newline at end of file
+</ul>
diff --git a/docs/html/tools/sdk/eclipse-adt.jd b/docs/html/tools/sdk/eclipse-adt.jd
index 52647ff..7b0b5a8 100644
--- a/docs/html/tools/sdk/eclipse-adt.jd
+++ b/docs/html/tools/sdk/eclipse-adt.jd
@@ -53,9 +53,46 @@
 <p>For a summary of all known issues in ADT, see <a
 href="http://tools.android.com/knownissues">http://tools.android.com/knownissues</a>.</p>
 
+
 <div class="toggle-content opened">
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+      alt=""/>ADT 22.0.4</a> <em>(July 2013)</em>
+  </p>
+
+  <div class="toggle-content-toggleme">
+<dl>
+  <dt>Dependencies:</dt>
+
+  <dd>
+    <ul>
+      <li>Java 1.6 or higher is required for ADT 22.0.4.</li>
+      <li>Eclipse Helios (Version 3.6.2) or higher is required for ADT 22.0.4.</li>
+      <li>ADT 22.0.4 is designed for use with <a href="{@docRoot}tools/sdk/tools-notes.html">SDK
+      Tools r22.0.4</a>. If you haven't already installed SDK Tools r22.0.4 into your SDK, use the
+      Android SDK Manager to do so.</li>
+    </ul>
+  </dd>
+
+  <dt>General Notes:</dt>
+  <dd>
+    <ul>
+      <li>Fixed problem with compiling Renderscript code.</li>
+      <li>Improved Gradle export with better workflow and error reporting.</li>
+      <li>Improved Gradle multi-module export feature.</li>
+      <li>Updated build logic to force exporting of the classpath containers unless you are using
+        the Maven plugin.</li>
+    </ul>
+  </dd>
+
+</dl>
+</div>
+</div>
+
+
+<div class="toggle-content closed">
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
       alt=""/>ADT 22.0.1</a> <em>(May 2013)</em>
   </p>
 
diff --git a/docs/html/tools/sdk/ndk/index.jd b/docs/html/tools/sdk/ndk/index.jd
index 74caaf4..1f34987 100644
--- a/docs/html/tools/sdk/ndk/index.jd
+++ b/docs/html/tools/sdk/ndk/index.jd
@@ -1,29 +1,29 @@
 ndk=true
 page.template=sdk
 
-ndk.mac64_download=android-ndk-r8e-darwin-x86_64.tar.bz2
-ndk.mac64_bytes=508419298
-ndk.mac64_checksum=efac96fab20e6ddb1311d6ba5648ce72
+ndk.mac64_download=android-ndk-r9-darwin-x86_64.tar.bz2
+ndk.mac64_bytes=726430529
+ndk.mac64_checksum=b975271d8f064611e7e12bf87b736826
 
-ndk.mac32_download=android-ndk-r8e-darwin-x86.tar.bz2
-ndk.mac32_bytes=496238878
-ndk.mac32_checksum=e17e707464c45c0d5615e4d0ae6a5cf7
+ndk.mac32_download=android-ndk-r9-darwin-x86.tar.bz2
+ndk.mac32_bytes=710781553
+ndk.mac32_checksum=6f7c4dd38df9079bb4b13846add5c0da
 
-ndk.linux64_download=android-ndk-r8e-linux-x86_64.tar.bz2
-ndk.linux64_bytes=466853553
-ndk.linux64_checksum=fa812352956067e7a9eefc0274675e9a
+ndk.linux64_download=android-ndk-r9-linux-x86_64.tar.bz2
+ndk.linux64_bytes=669064468
+ndk.linux64_checksum=3eedc86b20ec09fcd1fd03f4481a706d
 
-ndk.linux32_download=android-ndk-r8e-linux-x86.tar.bz2
-ndk.linux32_bytes=461526099
-ndk.linux32_checksum=26d774b0884bcd98de08eb4de41ab532
+ndk.linux32_download=android-ndk-r9-linux-x86.tar.bz2
+ndk.linux32_bytes=660787157
+ndk.linux32_checksum=999d155ba772c49baacee6d41d664922
 
-ndk.win64_download=android-ndk-r8e-windows-x86_64.zip
-ndk.win64_bytes=461298980
-ndk.win64_checksum=11eb99b3b56fc86d9d231ebff5c41db3
+ndk.win64_download=android-ndk-r9-windows-x86_64.zip
+ndk.win64_bytes=826661995
+ndk.win64_checksum=cd56cc1036235f16369f2112fa27be91
 
-ndk.win32_download=android-ndk-r8e-windows-x86.zip
-ndk.win32_bytes=434701805
-ndk.win32_checksum=fb41ed2bff5610b14a7b6f085ab86213
+ndk.win32_download=android-ndk-r9-windows-x86.zip
+ndk.win32_bytes=777938252
+ndk.win32_checksum=9c1f66ff963cc61e338964c5f97a4d34
 
 page.title=Android NDK
 @jd:body
@@ -255,13 +255,286 @@
 
 <h2 id="Revisions">Revisions</h2>
 
-<p>The sections below provide information and notes about successive releases of
-the NDK, as denoted by revision number. </p>
-
+<p>The following sections provide information about releases of the NDK.</p>
 
 <div class="toggle-content opened">
+  <p>
+    <a href="#" onclick="return toggleContent(this)"> <img
+      src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img" alt=""
+    >Android NDK, Revision 9</a> <em>(July 2013)</em>
+  </p>
+  <div class="toggle-content-toggleme">
+    <dl>
+      <dt>Important changes:</dt>
+      <dd>
+        <ul>
+          <li>Added support for Android 4.3 (API level 18). For more information, see
+            {@code STABLE-APIS.html} and new code examples in {@code samples/gles3jni/README}.
+          <li>Added headers and libraries for OpenGL ES 3.0, which is supported by Android 4.3
+            (API level 18) and higher.</li>
+          <li>Added GNU Compiler Collection (GCC) 4.8 compiler to the NDK. Since GCC 4.6 is still
+            the default, you must explicitly enable this option:
+            <ul>
+              <li>For {@code ndk-build} builds, export {@code NDK_TOOLCHAIN_VERSION=4.8} or
+                add it in {@code Application.mk}.</li>
+              <li>For standalone builds, use the {@code --toolchain=} option in
+                {@code make-standalone-toolchain.sh}, for example:<br>
+                {@code --toolchain=arm-linux-androideabi-4.8}</li>
+            </ul>
+            <p class="note"><strong>Note:</strong>
+            The {@code -Wunused-local-typedefs} option is enabled by {@code -Wall}. Be
+            sure to add {@code __attribute__((unused))} if you use compile-time asserts like
+            {@code sources/cxx-stl/stlport/stlport/stl/config/features.h}, line #311. For more
+            information, see
+            <a href="https://android-review.googlesource.com/#/c/55460">Change 55460</a></p>
+            <p class="note"><strong>Note:</strong>
+            In the GCC 4.7 release and later, ARM compilers generate unaligned access code by
+            default for ARMv6 and higher build targets. You may need to add the
+            {@code -mno-unaligned-access} build option when building for kernels that do not support
+            this feature.</p>
+          </li>
+          <li>Added Clang 3.3 support. The {@code NDK_TOOLCHAIN_VERSION=clang} build option
+            now picks Clang 3.3 by default.
+            <p class="note"><strong>Note:</strong>
+             Both GCC 4.4.3 and Clang 3.1 are deprecated, and will be removed from the next NDK
+             release.</p></li>
+          <li>Updated GNU Project Debugger (GDB) to support python 2.7.5.</li>
+          <li>Added MCLinker to support Windows hosts. Since {@code ld.gold}
+            is the default where available, you must add {@code -fuse-ld=mcld} in
+            {@code LOCAL_LDFLAGS} or {@code APP_LDFLAGS} to enable MCLinker.</li>
+          <li>Added {@code ndk-depends} tool which prints ELF library dependencies.
+            For more information, see {@code NDK-DEPENDS.html}.
+            (<a href="http://b.android.com/53486">Issue 53486</a>)</li>
+        </ul>
+      </dd>
+
+      <dt>Important bug fixes:</dt>
+      <dd>
+        <ul>
+          <li>Fixed potential event handling issue in {@code android_native_app_glue}.
+            (<a href="http://b.android.com/41755">Issue 41755</a>)</li>
+          <li>Fixed ARM/GCC-4.7 build to generate sufficient alignment for NEON load and store
+            instructions VST and VLD.
+            (<a href="http://gcc.gnu.org/bugzilla/show_bug.cgi?id=57271">GCC Issue 57271</a>)</li>
+          <li>Fixed a GCC 4.4.3/4.6/4.7 internal compiler error (ICE) for a constant negative index
+            value on a string literal.
+            (<a href="http://b.android.com/54623">Issue 54623</a>)</li>
+          <li>Fixed GCC 4.7 segmentation fault for constant initialization with an object address.
+            (<a href="http://b.android.com/56508">Issue 56508</a>)</li>
+          <li>Fixed GCC 4.6 ARM segmentation fault for <code>-O</code> values when using Boost
+            1.52.0. (<a href="http://b.android.com/42891">Issue 42891</a>)
+          <li>Fixed {@code libc.so} and {@code libc.a} to support the {@code wait4()} function.
+            (<a href="http://b.android.com/19854">Issue 19854</a>)</li>
+          <li>Updated the x86 libc.so and libc.a files to include the {@code clone()}
+            function.</li>
+          <li>Fixed {@code LOCAL_SHORT_COMMANDS} bug where the {@code linker.list} file is
+            empty or not used.</li>
+          <li>Fixed GCC MIPS build on Mac OS to use CFI directives, without which
+            {@code ld.mcld --eh-frame-hdr} fails frequently.</li>
+          <li>Fixed Clang 3.2 X86/MIPS internal compiler error in {@code llvm/lib/VMCore/Value.cpp}.
+            (<a href="https://android-review.googlesource.com/#/c/59021">Change 59021</a>)</li>
+          <li>Fixed GCC 4.7 64-bit Windows assembler crash. (Error: {@code out of memory allocating
+            4294967280 bytes}).</li>
+          <li>Updated {@code ndk-gdb} script so that the {@code --start} or {@code --launch} actions
+            now wait for the GNU Debug Server, so that it can more reliably hit breakpoints set
+            early in the execution path (such as breakpoints in JNI code).
+            (<a href="http://b.android.com/41278">Issue 41278</a>)
+            <p class="note"><strong>Note:</strong>
+              This feature requires jdb and produces warning about pending breakpoints.
+              Specify the {@code --nowait} option to restore previous behavior.
+            </p>
+          </li>
+          <li>Fixed GDB crash when library list is empty.</li>
+          <li>Fixed GDB crash when using a {@code stepi} command past a {@code bx pc} or
+            {@code blx pc} Thumb instruction.
+            (<a href="http://b.android.com/56962">Issue 56962</a>,
+             <a href="http://b.android.com/36149">Issue 36149</a>)</li>
+          <li>Fixed MIPS {@code gdbserver} to look for {@code DT_MIPS_RLD_MAP} instead of
+            {@code DT_DEBUG}. (<a href="http://b.android.com/56586">Issue 56586</a>)</li>
+          <li>Fixed a circular dependency in the ndk-build script, for example: If A-&gt;B and
+            B-&gt;B, then B was dropped from build.
+            (<a href="http://b.android.com/56690">Issue 56690</a>)</li>
+        </ul>
+      </dd>
+
+      <dt>Other bug fixes:</dt>
+      <dd>
+        <ul>
+          <li>Fixed the {@code ndk-build} script to enable you to specify a version of Clang as a
+            command line option (e.g., {@code NDK_TOOLCHAIN_VERSION=clang3.2}). Previously, only
+            specifying the version as an environment variable worked.</li>
+          <li>Fixed gabi++ size of {@code _Unwind_Exception} to be 24 for MIPS build targets when
+            using the Clang compiler.
+            (<a href="https://android-review.googlesource.com/#/c/54141">Change 54141</a>)</li>
+          <li>Fixed the {@code ndk-build} script to ensure that built libraries are actually
+            removed from projects that include prebuilt static libraries when using the
+            {@code ndk-build clean} command.
+            (<a href="https://android-review.googlesource.com/#/c/54461">Change 54461</a>,
+             <a href="https://android-review.googlesource.com/#/c/54480">Change 54480</a>)</li>
+          <li>Modified the {@code NDK_ANALYZE=1} option to be less verbose.</li>
+          <li>Fixed {@code gnu-libstdc++/Android.mk} to include a {@code backward/} path for builds
+            that use backward compability.
+            (<a href="http://b.android.com/53404">Issue 53404</a>)</li>
+          <li>Fixed a problem where {@code stlport new} sometimes returned random values.</li>
+          <li>Fixed {@code ndk-gdb} to match the order of {@code CPU_ABIS}, not {@code APP_ABIS}.
+            (<a href="http://b.android.com/54033">Issue 54033</a>)</li>
+          <li>Fixed a problem where the NDK 64-bit build on MacOSX choses the wrong path for
+            compiler.
+            (<a href="http://b.android.com/53769">Issue 53769</a>)</li>
+          <li>Fixed build scripts to detect 64-bit Windows Vista.
+            (<a href="http://b.android.com/54485">Issue 54485</a>)</li>
+          <li>Fixed x86 {@code ntonl/swap32} error: {@code invalid 'asm': operand number
+            out of range}.
+            (<a href="http://b.android.com/54465">Issue 54465</a>,
+             <a href="https://android-review.googlesource.com/#/c/57242">Change 57242</a>)</li>
+          <li>Fixed {@code ld.gold} to merge string literals.</li>
+          <li>Fixed {@code ld.gold} to handle large symbol alignment.</li>
+          <li>Updated {@code ld.gold} to enable the {@code --sort-section=name} option.</li>
+          <li>Fixed GCC 4.4.3/4.6/4.7 to suppress the {@code -export-dynamic} option for
+            statically linked programs. GCC no longer adds an {@code .interp} section for statically
+            linked programs.</li>
+          <li>Fixed GCC 4.4.3 {@code stlport} compilation error about inconsistent {@code typedef}
+            of {@code _Unwind_Control_Block}.
+            (<a href="http://b.android.com/54426">Issue 54426</a>)</li>
+          <li>Fixed {@code awk} scripts to handle {@code AndroidManifest.xml} files created on
+            Windows which may contain trailing {@code \r} characters and cause build errors.
+            (<a href="http://b.android.com/42548">Issue 42548</a>)</li>
+          <li>Fixed {@code make-standalone-toolchain.sh} to probe the {@code prebuilts/}
+            directory to detect if the host is 32 bit or 64 bit.</li>
+          <li>Fixed the Clang 3.2 {@code -integrated-as} option.</li>
+          <li>Fixed the Clang 3.2 ARM EHABI compact model {@code pr1} and {@code pr2} handler data.
+            </li>
+          <li>Added clang {@code -mllvm -arm-enable-ehabi} option to fix the following clang error:
+            <pre>clang: for the -arm-enable-ehabi option: may only occur zero or one times!</pre>
+            </li>
+          <li>Fixed build failure when there is no {@code uses-sdk} element in application
+            manifest. (<a href="http://b.android.com/57015">Issue 57015</a>)</li>
+        </ul>
+
+      </dd>
+      <dt>Other changes:</dt>
+      <dd>
+        <ul>
+          <li>Header Fixes
+            <ul>
+              <li>Modified headers to make {@code __set_errno} an inlined function, since
+                {@code __set_errno} in {@code errno.h} is deprecated, and {@code libc.so} no longer
+                exports it.</li>
+              <li>Modified {@code elf.h} to include {@code stdint.h}.
+                (<a href="http://b.android.com/55443">Issue 55443</a>)</li>
+              <li>Fixed {@code sys/un.h} to be included independently of other headers.
+                (<a href="http://b.android.com/53646">Issue 53646</a>)</li>
+              <li>Fixed all of the {@code MotionEvent_getHistorical} API family to take the
+                {@code const AInputEvent* motion_event}.
+                (<a href="http://b.android.com/55873">Issue 55873</a>)</li>
+              <li>Fixed {@code malloc_usable_size} to take {@code const void*}.
+                (<a href="http://b.android.com/55725">Issue 55725</a>)</li>
+              <li>Fixed stdint.h to be more compatible with C99.
+                (<a href="https://android-review.googlesource.com/#/c/46821">Change 46821</a>)</li>
+              <li>Modified {@code wchar.h} to not redefine {@code WCHAR_MAX} and
+                {@code WCHAR_MIN}</li>
+              <li>Fixed {@code &lt;inttypes.h&gt;} declaration for pointer-related {@code PRI} and
+                {@code SCN} macros. (<a href="http://b.android.com/57218">Issue 57218</a>)</li>
+              <li>Changed the {@code sys/cdefs.h} header so that {@code __WCHAR_TYPE__} is 32-bit
+                for API levels less than 9, which means that {@code wchat_t} is 32-bit for all
+                API levels. To restore the previous behavior, define the {@code _WCHAR_IS_8BIT}
+                boolean variable. (<a href="http://b.android.com/57267">Issue 57267</a>)</li>
+            </ul>
+          </li>
+          <li>Added more formatting in NDK {@code docs/} and miscellaneous documentation fixes.
+            </li>
+          <li>Added support for a thin archive technique when building static libraries.
+            (<a href="http://b.android.com/40303">Issue 40303</a>)</li>
+          <li>Updated script {@code make-standalone-toolchain.sh} to support the {@code stlport}
+            library in addition to {@code gnustl}, when you specify the option
+            {@code --stl=stlport}. For more information, see {@code STANDALONE-TOOLCHAIN.html}.</li>
+          <li>Updated the {@code make-standalone-toolchain.sh} script so that the
+            {@code --llvm-version=} option creates the {@code $TOOLCHAIN_PREFIX-clang} and
+            {@code $TOOLCHAIN_PREFIX-clang++} scripts in addition to {@code clang} and
+            {@code clang++}, to avoid using the host's clang and clang++ definitions by accident.
+            </li>
+          <li>Added two flags to re-enable two optimizations in upstream Clang but disabled in
+              NDK for better compatibility with code compiled by GCC:
+            <ul>
+              <li>Added a {@code -fcxx-missing-return-semantics} flag to re-enable <em>missing return
+                semantics</em> in Clang 3.2+. Normally, all paths should terminate with a return
+                statement for a value-returning function. If this is not the case, clang inserts
+                an undefined instruction (or trap in debug mode) at the path without a return
+                statement. If you are sure your code is correct, use this flag to allow the
+                optimizer to take advantage of the undefined behavior. If you are not sure, do not
+                use this flag. The caller may still receive a random incorrect value, but the
+                optimizer will not exploit it and make your code harder to debug.</li>
+              <li>Added a {@code -fglobal-ctor-const-promotion} flag to re-enable
+                promoting global variables with static constructor to be constants. With this flag,
+                the global variable optimization pass of LLVM tries to evaluate the global
+                variables with static constructors and promote them to global constants. Although
+                this optimization is correct, it may cause some incompatability with code compiled
+                by GCC. For example, code may do {@code const_cast} to cast the constant to mutable
+                and modify it. In GCC, the variable is in read-write and the code is run by
+                accident. In Clang, the const variable is in read-only memory and may cause your
+                application to crash.</li>
+            </ul>
+          </li>
+          <li>Added {@code -mldc1-sdc1} to the MIPS GCC and Clang compilers. By default, compilers
+            align 8-byte objects properly and emit the {@code ldc1} and {@code sdc1} instructions
+            to move them around. If your app uses a custom allocator that does not always align
+            with a new object's 8-byte boundary in the same way as the default allocator, your app
+            may crash due to {@code ldc1} and {@code sdc1} operations on unaligned memory. In this
+            case, use the {@code -mno-ldc1-sdc1} flag to workaround the problem.</li>
+          <li>Downgraded the event severity from warning to info if {@code APP_PLATFORM_LEVEL} is
+            larger than {@code APP_MIN_PLATFORM_LEVEL}. The {@code APP_PLATFORM_LEVEL} may be lower
+            than {@code APP_PLATFORM} in {@code jni/Application.mk} because the NDK does not have
+            headers for all levels. In this case, the actual level is shifted downwards. The
+            {@code APP_MIN_PLATFORM_LEVEL} is specified by the {@code android:minSdkVersion} in
+            your application's manifest.
+            (<a href="http://b.android.com/39752">Issue 39752</a>)</li>
+          <li>Added the {@code android_getCpuIdArm()} and {@code android_setCpuArm()} methods to
+            {@code cpu-features.c}. This addition enables easier retrieval of the ARM CPUID
+            information. (<a href="http://b.android.com/53689">Issue 53689</a>)</li>
+          <li>Modified {@code ndk-build} to use GCC 4.7's {@code as/ld} for Clang compiling.
+            <p class="note"><strong>Note:</strong>
+              In GCC 4.7, {@code monotonic_clock} and {@code is_monotonic} have been renamed to
+              {@code steady_clock} and {@code is_steady}, respectively.</p></li>
+          <li>Added the following new warnings to the {@code ndk-build} script:
+            <ul>
+              <li>Added warnings if {@code LOCAL_LDLIBS/LDFLAGS} are used in static library
+                modules.</li>
+              <li>Added a warning if a configuration has no module to build.</li>
+              <li>Added a warning for non-system libraries being used in
+                {@code LOCAL_LDLIBS/LDFLAGS} of a shared library or executable modules.</li>
+            </ul>
+          </li>
+          <li>Updated build scripts, so that if {@code APP_MODULES} is not defined and only static
+            libraries are listed in {@code Android.mk}, the script force-builds all of them.
+            (<a href="http://b.android.com/53502">Issue 53502</a>)</li>
+          <li>Updated {@code ndk-build} to support absolute paths in {@code LOCAL_SRC_FILES}.</li>
+          <li>Removed the {@code *-gdbtui} executables, which are duplicates of the {@code *-gdb}
+            executables with the {@code -tui} option enabled.</li>
+          <li>Updated the build scripts to warn you when the Edison Design Group (EDG) compiler
+            front-end turns {@code _STLP_HAS_INCLUDE_NEXT} back on.
+            (<a href="http://b.android.com/53646">Issue 53646</a>)</li>
+          <li>Added the environment variable {@code NDK_LIBS_OUT} to allow overriding of the
+            path for {@code libraries/gdbserver} from the default {@code $PROJECT/libs}.
+            For more information, see {@code OVERVIEW.html}.</li>
+          <li>Changed ndk-build script defaults to compile code with format string protection
+            {@code -Wformat -Werror=format-security}. You may set
+            {@code LOCAL_DISABLE_FORMAT_STRING_CHECKS=true} to disable it.
+            For more information, see {@code ANDROID-MK.html}</li>
+          <li>Added STL pretty-print support in {@code ndk-gdb-py}. For more information, see
+            {@code NDK-GDB.html}.</li>
+          <li>Added tests based on the googletest frameworks.</li>
+          <li>Added a notification to the toolchain build script that warns you if the current shell
+            is not {@code bash}.</li>
+        </ul>
+      </dd>
+    </dl>
+  </div>
+</div>
+
+
+<div class="toggle-content closed">
   <p><a href="#" onclick="return toggleContent(this)">
-    <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
       alt="">Android NDK, Revision 8e</a> <em>(March 2013)</em>
   </p>
 
@@ -283,7 +556,7 @@
             build automatically sorts out the order of libraries specified in
             {@code LOCAL_STATIC_LIBRARIES}, {@code LOCAL_WHOLE_STATIC_LIBRARIES} and
             {@code LOCAL_SHARED_LIBRARIES}. For more information, see {@code CHANGES.HTML}.
-            (<a href="http://code.google.com/p/android/issues/detail?id=39378">Issue 39378</a>)</li>
+            (<a href="http://b.android.com/39378">Issue 39378</a>)</li>
         </ul>
       </dd>
 
@@ -295,23 +568,23 @@
           <li>Fixed build script which unconditionally builds Clang/llvm for MacOSX in 64-bit.</li>
           <li>Fixed GCC 4.6/4.7 internal compiler error:
             {@code gen_thumb_movhi_clobber at config/arm/arm.md:5832}.
-            (<a href="http://code.google.com/p/android/issues/detail?id=52732">Issue 52732</a>)</li>
+            (<a href="http://b.android.com/52732">Issue 52732</a>)</li>
           <li>Fixed build problem where GCC/ARM 4.6/4.7 fails to link code using 64-bit atomic
             built-in functions.
-            (<a href="http://code.google.com/p/android/issues/detail?id=41297">Issue 41297</a>)</li>
+            (<a href="http://b.android.com/41297">Issue 41297</a>)</li>
           <li>Fixed GCC 4.7 linker DIV usage mismatch errors.
           (<a href="http://sourceware.org/ml/binutils/2012-12/msg00202.html">Sourceware Issue</a>)
           <li>Fixed GCC 4.7 internal compiler error {@code build_data_member_initialization, at
             cp/semantics.c:5790}.</li>
           <li>Fixed GCC 4.7 internal compiler error {@code redirect_eh_edge_1, at tree-eh.c:2214}.
-            (<a href="http://code.google.com/p/android/issues/detail?id=52909">Issue 52909</a>)</li>
+            (<a href="http://b.android.com/52909">Issue 52909</a>)</li>
           <li>Fixed a GCC 4.7 segfault.
             (<a href="http://gcc.gnu.org/bugzilla/show_bug.cgi?id=55245">GCC Issue</a>)</li>
           <li>Fixed {@code &lt;chrono&gt;} clock resolution and enabled {@code steady_clock}.
-            (<a href="http://code.google.com/p/android/issues/detail?id=39680">Issue 39680</a>)</li>
+            (<a href="http://b.android.com/39680">Issue 39680</a>)</li>
           <li>Fixed toolchain to enable {@code _GLIBCXX_HAS_GTHREADS} for GCC 4.7 libstdc++.
-            (<a href="http://code.google.com/p/android/issues/detail?id=41770">Issue 41770</a>,
-             <a href="http://code.google.com/p/android/issues/detail?id=41859">Issue 41859</a>)</li>
+            (<a href="http://b.android.com/41770">Issue 41770</a>,
+             <a href="http://b.android.com/41859">Issue 41859</a>)</li>
           <li>Fixed problem with the X86 MXX/SSE code failing to link due to missing
             {@code posix_memalign}.
             (<a href="https://android-review.googlesource.com/#/c/51872">Change 51872</a>)</li>
@@ -321,24 +594,24 @@
           <li>Fixed GCC4.7/X86 to restore earlier {@code cmov} behavior.
             (<a href="http://gcc.gnu.org/viewcvs?view=revision&revision=193554">GCC Issue</a>)</li>
           <li>Fixed handling NULL return value of {@code setlocale()} in libstdc++/GCC4.7.
-            (<a href="http://code.google.com/p/android/issues/detail?id=46718">Issue 46718</a>)
+            (<a href="http://b.android.com/46718">Issue 46718</a>)
           <li>Fixed {@code ld.gold} runtime undefined reference to {@code __exidx_start} and
             {@code __exidx_start_end}.
             (<a href="https://android-review.googlesource.com/#/c/52134">Change 52134</a>)</li>
           <li>Fixed Clang 3.1 internal compiler error when using Eigen library.
-            (<a href="http://code.google.com/p/android/issues/detail?id=41246">Issue 41246</a>)</li>
+            (<a href="http://b.android.com/41246">Issue 41246</a>)</li>
           <li>Fixed Clang 3.1 internal compiler error including {@code &lt;chrono&gt;} in C++11 mode.
-            (<a href="http://code.google.com/p/android/issues/detail?id=39600">Issue 39600</a>)</li>
+            (<a href="http://b.android.com/39600">Issue 39600</a>)</li>
           <li>Fixed Clang 3.1 internal compiler error when generating object code for a method
             call to a uniform initialized {@code rvalue}.
-            (<a href="http://code.google.com/p/android/issues/detail?id=41387">Issue 41387</a>)</li>
+            (<a href="http://b.android.com/41387">Issue 41387</a>)</li>
           <li>Fixed Clang 3.1/X86 stack realignment.
             (<a href="https://android-review.googlesource.com/#/c/52154">Change 52154</a>)</li>
           <li>Fixed problem with GNU Debugger (GDB) SIGILL when debugging on Android 4.1.2.
-            (<a href="http://code.google.com/p/android/issues/detail?id=40941">Issue 40941</a>)</li>
+            (<a href="http://b.android.com/40941">Issue 40941</a>)</li>
           <li>Fixed problem where GDB cannot set {@code source:line} breakpoints when symbols contain
             long, indirect file paths.
-            (<a href="http://code.google.com/p/android/issues/detail?id=42448">Issue 42448</a>)</li>
+            (<a href="http://b.android.com/42448">Issue 42448</a>)</li>
           <li>Fixed GDB {@code read_program_header} for MIPS PIE executables.
             (<a href="https://android-review.googlesource.com/#/c/49592">Change 49592</a>)</li>
           <li>Fixed {@code STLport} segmentation fault in {@code uncaught_exception()}.
@@ -346,7 +619,7 @@
           <li>Fixed {@code STLport} bus error in exception handling due to unaligned access of
             {@code DW_EH_PE_udata2}, {@code DW_EH_PE_udata4}, and {@code DW_EH_PE_udata8}.</li>
           <li>Fixed Gabi++ infinite recursion problem with {@code nothrow new[]} operator.
-            (<a href="http://code.google.com/p/android/issues/detail?id=52833">Issue 52833</a>)</li>
+            (<a href="http://b.android.com/52833">Issue 52833</a>)</li>
           <li>Fixed Gabi++ wrong offset to exception handler pointer.
             (<a href="https://android-review.googlesource.com/#/c/53446">Change 53446</a>)</li>
           <li>Removed Gabi++ redundant free on exception object
@@ -365,11 +638,11 @@
               <li>Fixed {@code stddef.h} to not redefine {@code offsetof} since it already exists
                 in the toolchain.</li>
               <li>Fixed {@code elf.h} to contain {@code Elf32_auxv_t} and {@code Elf64_auxv_t}.
-                (<a href="http://code.google.com/p/android/issues/detail?id=38441">Issue 38441</a>)
+                (<a href="http://b.android.com/38441">Issue 38441</a>)
                 </li>
               <li>Fixed the {@code #ifdef} C++ definitions in the
                 {@code OpenSLES_AndroidConfiguration.h} header file.
-                (<a href="http://code.google.com/p/android/issues/detail?id=53163">Issue 53163</a>)
+                (<a href="http://b.android.com/53163">Issue 53163</a>)
                 </li>
             </ul>
           </li>
@@ -377,7 +650,7 @@
             </li>
           <li>Fixed system and Gabi++ headers to be able to compile with API level 8 and lower.</li>
           <li>Fixed {@code cpufeatures} to not parse {@code /proc/self/auxv}.
-            (<a href="http://code.google.com/p/android/issues/detail?id=43055">Issue 43055</a>)</li>
+            (<a href="http://b.android.com/43055">Issue 43055</a>)</li>
           <li>Fixed {@code ld.gold} to not depend on host libstdc++ and on Windows platforms,
             to not depend on the {@code libgcc_sjlj_1.dll} library.</li>
           <li>Fixed Clang 3.1 which emits inconsistent register list in {@code .vsave} and fails
@@ -394,16 +667,16 @@
             </li>
           <li>Fixed X86 {@code libc.so} and {@code lib.a} which were missing the {@code sigsetjmp}
             and {@code siglongjmp} functions already declared in {@code setjmp.h}.
-            (<a href="http://code.google.com/p/android/issues/detail?id=19851">Issue 19851</a>)</li>
+            (<a href="http://b.android.com/19851">Issue 19851</a>)</li>
           <li>Patched GCC 4.4.3/4.6/4.7 libstdc++ to work with Clang in C++ 11.
             (<a href="http://clang.llvm.org/cxx_status.html">Clang Issue</a>)</li>
           <li>Fixed cygwin path in argument passed to {@code HOST_AWK}.</li>
           <li>Fixed {@code ndk-build} script warning in windows when running from project's JNI
             directory.
-            (<a href="http://code.google.com/p/android/issues/detail?id=40192">Issue 40192</a>)</li>
+            (<a href="http://b.android.com/40192">Issue 40192</a>)</li>
           <li>Fixed problem where the {@code ndk-build} script does not build if makefile has
             trailing whitespace in the {@code LOCAL_PATH} definition.
-            (<a href="http://code.google.com/p/android/issues/detail?id=42841">Issue 42841</a>)</li>
+            (<a href="http://b.android.com/42841">Issue 42841</a>)</li>
         </ul>
       </dd>
 
@@ -419,13 +692,13 @@
             hidden visibility except for exception handling helpers.</li>
           <li>Updated build so that {@code STLport} is built for ARM in Thumb mode.</li>
           <li>Added support for {@code std::set_new_handler} in Gabi++.
-            (<a href="http://code.google.com/p/android/issues/detail?id=52805">Issue 52805</a>)</li>
+            (<a href="http://b.android.com/52805">Issue 52805</a>)</li>
           <li>Enabled {@code FUTEX} system call in GNU libstdc++.</li>
           <li>Updated {@code ndk-build} so that it  no longer copies prebuilt static library to
             a project's {@code obj/local/&lt;abi&gt;/} directory.
-            (<a href="http://code.google.com/p/android/issues/detail?id=40302">Issue 40302</a>)</li>
+            (<a href="http://b.android.com/40302">Issue 40302</a>)</li>
           <li>Removed {@code __ARM_ARCH_5*__} from ARM {@code toolchains/*/setup.mk} script.
-            (<a href="http://code.google.com/p/android/issues/detail?id=21132">Issue 21132</a>)</li>
+            (<a href="http://b.android.com/21132">Issue 21132</a>)</li>
           <li>Built additional GNU libstdc++ libraries in thumb for ARM.</li>
           <li>Enabled MIPS floating-point {@code madd/msub/nmadd/nmsub/recip/rsqrt}
             instructions with 32-bit FPU.</li>
@@ -458,7 +731,7 @@
                 which was preventing a significant amount of parallel build processing.</li>
               <li>Updated {@code build-gabi++.sh} and {@code build-stlport.sh} so they can now run
                 from the NDK package.
-                (<a href="http://code.google.com/p/android/issues/detail?id=52835">Issue 52835</a>)
+                (<a href="http://b.android.com/52835">Issue 52835</a>)
                 </li>
               <li>Fixed {@code run-tests.sh} in the {@code MSys} utilities collection.</li>
               <li>Improved 64-bit host toolchain and Canadian Cross build support.</li>
@@ -540,7 +813,7 @@
       <dd>
         <ul>
           <li>Fixed unnecessary rebuild of object files when using the {@code ndk-build} script.
-            (<a href="http://code.google.com/p/android/issues/detail?id=39810">Issue 39810</a>)</li>
+            (<a href="http://b.android.com/39810">Issue 39810</a>)</li>
           <li>Fixed a linker failure with the NDK 8c release for Mac OS X 10.6.x that produced the
             following error:
             <pre>
@@ -551,29 +824,29 @@
             not compatible with Mac OS 10.6.x and the NDK.
           </li>
           <li>Removed the {@code -x c++} options from the Clang++ standalone build script.
-          (<a href="http://code.google.com/p/android/issues/detail?id=39089">Issue 39089</a>)</li>
+          (<a href="http://b.android.com/39089">Issue 39089</a>)</li>
           <li>Fixed issues using the {@code NDK_TOOLCHAIN_VERSION=clang3.1} option in Cygwin.
-           (<a href="http://code.google.com/p/android/issues/detail?id=39585">Issue 39585</a>)</li>
+           (<a href="http://b.android.com/39585">Issue 39585</a>)</li>
           <li>Fixed the {@code make-standalone-toolchain.sh} script to allow generation of a
             standalone toolchain using the Cygwin or MinGW environments. The resulting toolchain
             can be used in Cygwin, MingGW or CMD.exe environments.
-            (<a href="http://code.google.com/p/android/issues/detail?id=39915">Issue 39915</a>,
-            <a href="http://code.google.com/p/android/issues/detail?id=39585">Issue 39585</a>)</li>
+            (<a href="http://b.android.com/39915">Issue 39915</a>,
+            <a href="http://b.android.com/39585">Issue 39585</a>)</li>
           <li>Added missing {@code SL_IID_ANDROIDBUFFERQUEUESOURCE} option in android-14 builds for
             ARM and X86.
-            (<a href="http://code.google.com/p/android/issues/detail?id=40625">Issue 40625</a>)</li>
+            (<a href="http://b.android.com/40625">Issue 40625</a>)</li>
           <li>Fixed x86 CPU detection for the {@code ANDROID_CPU_X86_FEATURE_MOVBE} feature.
-            (<a href="http://code.google.com/p/android/issues/detail?id=39317">Issue 39317</a>)</li>
+            (<a href="http://b.android.com/39317">Issue 39317</a>)</li>
           <li>Fixed an issue preventing the Standard Template Library (STL) from using C++
             sources that do not have a {@code .cpp} file extension.</li>
           <li>Fixed GCC 4.6 ARM internal compiler error <em>at reload1.c:1061</em>.
-            (<a href="http://code.google.com/p/android/issues/detail?id=20862">Issue 20862</a>)</li>
+            (<a href="http://b.android.com/20862">Issue 20862</a>)</li>
           <li>Fixed GCC 4.4.3 ARM internal compiler error <em>at emit-rtl.c:1954</em>.
-            (<a href="http://code.google.com/p/android/issues/detail?id=22336">Issue 22336</a>)</li>
+            (<a href="http://b.android.com/22336">Issue 22336</a>)</li>
           <li>Fixed GCC 4.4.3 ARM internal compiler error <em>at postreload.c:396</em>.
-            (<a href="http://code.google.com/p/android/issues/detail?id=22345">Issue 22345</a>)</li>
+            (<a href="http://b.android.com/22345">Issue 22345</a>)</li>
           <li>Fixed problem with GCC 4.6/4.7 skipping lambda functions.
-            (<a href="http://code.google.com/p/android/issues/detail?id=35933">Issue 35933</a>)</li>
+            (<a href="http://b.android.com/35933">Issue 35933</a>)</li>
         </ul>
       </dd>
 
@@ -584,21 +857,21 @@
             <ul>
               <li>Fixed {@code __WINT_TYPE__} and {@code wint_t} to be the same type.</li>
               <li>Corrected typo in {@code android/bitmap.h}.
-                (<a href="http://code.google.com/p/android/issues/detail?id=15134">Issue 15134</a>)
+                (<a href="http://b.android.com/15134">Issue 15134</a>)
               </li>
               <li>Corrected typo in {@code errno.h}.</li>
               <li>Added check for the presence of {@code __STDC_VERSION__} in {@code sys/cdefs.h}.
-                (<a href="http://code.google.com/p/android/issues/detail?id=14627">Issue 14627</a>)
+                (<a href="http://b.android.com/14627">Issue 14627</a>)
               </li>
               <li>Reorganized headers in {@code byteswap.h} and {@code dirent.h}.</li>
               <li>Fixed {@code limits.h} to include {@code page.h} which provides {@code PAGE_SIZE}
                 settings.
-                (<a href="http://code.google.com/p/android/issues/detail?id=39983">Issue 39983</a>)
+                (<a href="http://b.android.com/39983">Issue 39983</a>)
               </li>
               <li>Fixed return type of {@code glGetAttribLocation()} and
                 {@code glGetUniformLocation()} from {@code int} to {@code GLint}.</li>
               <li>Fixed {@code __BYTE_ORDER} constant for x86 builds.
-                (<a href="http://code.google.com/p/android/issues/detail?id=39824">Issue 39824</a>)
+                (<a href="http://b.android.com/39824">Issue 39824</a>)
               </li>
             </ul>
           </li>
@@ -611,7 +884,7 @@
           <li>Fixed ARM EHABI support in Clang to conform to specifications.</li>
           <li>Fixed GNU Debugger (GDB) to shorten the time spent on walking the target's link map
             during {@code solib} events.
-            (<a href="http://code.google.com/p/android/issues/detail?id=38402">Issue 38402</a>)</li>
+            (<a href="http://b.android.com/38402">Issue 38402</a>)</li>
           <li>Fixed missing {@code libgcc.a} file when linking shared libraries.</li>
         </ul>
       </dd>
@@ -712,7 +985,7 @@
         <ul>
           <li>Fixed an issue where running {@code make-standalone-toolchain.sh} with root privileges
             resulted in the stand alone tool chain being inaccessible to some users.
-            (<a href="http://code.google.com/p/android/issues/detail?id=35279">Issue 35279</a>)
+            (<a href="http://b.android.com/35279">Issue 35279</a>)
             <ul>
               <li>All files and executables in the NDK release package are set to have read and
                 execute permissions for all.</li>
@@ -722,23 +995,23 @@
           <li>Removed redundant {@code \r} from Windows prebuilt {@code echo.exe}. The redundant
           {@code \r} caused {@code gdb.setup} to fail in the GNU Debugger (GDB) because it
           incorrectly became part of the path.
-          (<a href="http://code.google.com/p/android/issues/detail?id=36054">Issue 36054</a>)</li>
+          (<a href="http://b.android.com/36054">Issue 36054</a>)</li>
           <li>Fixed Windows parallel builds that sometimes failed due to timing issues in the
           {@code host-mkdir} implementation.
-          (<a href="http://code.google.com/p/android/issues/detail?id=25875">Issue 25875</a>)</li>
+          (<a href="http://b.android.com/25875">Issue 25875</a>)</li>
           <li>Fixed GCC 4.4.3 GNU {@code libstdc++} to <em>not</em> merge {@code typeinfo} names by
           default. For more details, see
           {@code toolchain repo gcc/gcc-4.4.3/libstdc++-v3/libsupc++/typeinfo}.
-          (<a href="http://code.google.com/p/android/issues/detail?id=22165">Issue 22165</a>)</li>
+          (<a href="http://b.android.com/22165">Issue 22165</a>)</li>
           <li>Fixed problem on {@code null} context in GCC 4.6
           {@code cp/mangle.c::write_unscoped_name}, where GCC may crash when the context is
           {@code null} and dereferenced in {@code TREE_CODE}.</li>
           <li>Fixed GCC 4.4.3 crashes on ARM NEON-specific type definitions for floats.
-          (<a href="http://code.google.com/p/android/issues/detail?id=34613">Issue 34613</a>)</li>
+          (<a href="http://b.android.com/34613">Issue 34613</a>)</li>
           <li>Fixed the {@code STLport} internal {@code _IteWrapper::operator*()} implementation
           where a stale stack location holding the dereferenced value was returned and caused
           runtime crashes.
-          (<a href="http://code.google.com/p/android/issues/detail?id=38630">Issue 38630</a>)</li>
+          (<a href="http://b.android.com/38630">Issue 38630</a>)</li>
 
           <li>ARM-specific fixes:
             <ul>
@@ -755,17 +1028,17 @@
               <li>Fixed {@code binutils-2.21/ld.bfd} to be capable of linking object from older
               binutils without {@code tag_FP_arch}, which was producing <em>assertion fail</em>
               error messages in GNU Binutils.
-              (<a href="http://code.google.com/p/android/issues/detail?id=35209">Issue 35209</a>)
+              (<a href="http://b.android.com/35209">Issue 35209</a>)
               </li>
               <li>Removed <em>Unknown EABI object attribute 44</em> warning when
               {@code binutils-2.19/ld} links prebuilt object by newer {@code binutils-2.21}</li>
               <li>Fixed an issue in GNU {@code stdc++} compilation with both {@code -mthumb} and
               {@code -march=armv7-a}, by modifying {@code make-standalone-toolchain.sh} to populate
               {@code headers/libs} in sub-directory {@code armv7-a/thumb}.
-              (<a href="http://code.google.com/p/android/issues/detail?id=35616">Issue 35616</a>)
+              (<a href="http://b.android.com/35616">Issue 35616</a>)
               </li>
               <li>Fixed <em>unresolvable R_ARM_THM_CALL relocation</em> error.
-              (<a href="http://code.google.com/p/android/issues/detail?id=35342">Issue 35342</a>)
+              (<a href="http://b.android.com/35342">Issue 35342</a>)
               </li>
               <li>Fixed internal compiler error at {@code reload1.c:3633}, caused by the ARM
               back-end expecting the wrong operand type when sign-extend from {@code char}.
@@ -794,11 +1067,11 @@
               <li>Disabled Python support in gdb-7.x at build, otherwise the gdb-7.x configure
               function may pick up whatever Python version is available on the host and build
               {@code gdb} with a hard-wired dependency on a specific version of Python.
-              (<a href="http://code.google.com/p/android/issues/detail?id=36120">Issue 36120</a>)
+              (<a href="http://b.android.com/36120">Issue 36120</a>)
               </li>
               <li>Fixed {@code ndk-gdb} when {@code APP_ABI} contains {@code all} and matchs none
               of the known architectures.
-              (<a href="http://code.google.com/p/android/issues/detail?id=35392">Issue 35392</a>)
+              (<a href="http://b.android.com/35392">Issue 35392</a>)
               </li>
               <li>Fixed Windows pathname support, by keeping the {@code :} character if it looks
               like it could be part of a Windows path starting with a drive letter.
@@ -809,7 +1082,7 @@
               </li>
               <li>Added fix to only read the current {@code solibs} when the linker is consistent.
               This change speeds up {@code solib} event handling.
-              (<a href="http://code.google.com/p/android/issues/detail?id=37677">Issue 37677</a>)
+              (<a href="http://b.android.com/37677">Issue 37677</a>)
               </li>
               <li>Added fix to make repeated attempts to find {@code solib} breakpoints. GDB now
               retries {@code enable_break()} during every call to {@code svr4_current_sos()} until
@@ -817,13 +1090,13 @@
               (<a href="https://android-review.googlesource.com/#/c/43563">Change 43563</a>)</li>
               <li>Fixed an issue where {@code gdb} would not stop on breakpoints placed in
               {@code dlopen-ed} libraries.
-              (<a href="http://code.google.com/p/android/issues/detail?id=34856">Issue 34856</a>)
+              (<a href="http://b.android.com/34856">Issue 34856</a>)
               </li>
               <li>Fixed {@code SIGILL} in dynamic linker when calling {@code dlopen()}, on system
               where {@code /system/bin/linker} is stripped of symbols and
               {@code rtld_db_dlactivity()} is implemented as {@code Thumb}, due to not preserving
               {@code LSB} of {@code sym_addr}.
-              (<a href="http://code.google.com/p/android/issues/detail?id=37147">Issue 37147</a>)
+              (<a href="http://b.android.com/37147">Issue 37147</a>)
               </li>
             </ul>
           </li>
@@ -848,7 +1121,7 @@
               {@code __END_DECLS}.</li>
               <li>Removed unimplemented functions in {@code malloc.h}.</li>
               <li>Fixed {@code stdint.h} defintion of {@code uint64_t} for ANSI compilers.
-              (<a href="http://code.google.com/p/android/issues/detail?id=1952">Issue 1952</a>)</li>
+              (<a href="http://b.android.com/1952">Issue 1952</a>)</li>
               <li>Fixed preprocessor macros in {@code &lt;arch&gt;/include/machine/*}.</li>
               <li>Replaced {@code link.h} for MIPS with new version supporting all platforms.</li>
               <li>Removed {@code linux-unistd.h}</li>
@@ -904,7 +1177,7 @@
           {@code platforms/android-[3,4,5,8]}. Those headers were incomplete, since both X86 and
           MIPS ABIs are only supported at API 9 or higher.</li>
           <li>Simplified c++ include path in standalone packages, as shown below.
-          (<a href="http://code.google.com/p/android/issues/detail?id=35279">Issue 35279</a>)
+          (<a href="http://b.android.com/35279">Issue 35279</a>)
 <pre>
 &lt;path&gt;/arm-linux-androideabi/include/c++/4.6.x-google
   to:
@@ -916,7 +1189,7 @@
           <li>Fixed an issue in {@code samples/san-angeles} that caused a black screen or freeze
           frame on re-launch.</li>
           <li>Replaced deprecated APIs in NDK samples.
-          (<a href="http://code.google.com/p/android/issues/detail?id=20017">Issue 20017</a>)
+          (<a href="http://b.android.com/20017">Issue 20017</a>)
             <ul>
               <li>{@code hello-gl2} from android-5 to android-7</li>
               <li>{@code native-activity} from android-9 to android-10</li>
@@ -1196,7 +1469,7 @@
           <li>Fixed a typo in GAbi++ implementation where the result of {@code
           dynamic_cast&lt;D&gt;(b)} of base class object {@code b} to derived class {@code D} is
           incorrectly adjusted in the opposite direction from the base class.
-          (<a href="http://code.google.com/p/android/issues/detail?id=28721">Issue 28721</a>)
+          (<a href="http://b.android.com/28721">Issue 28721</a>)
           </li>
           <li>Fixed an issue in which {@code make-standalone-toolchain.sh} fails to copy
           {@code libsupc++.*}.</li>
@@ -1710,7 +1983,7 @@
             <li>Fixed the standalone toolchain linker warnings about missing the definition and
             size for the <code>__dso_handle</code> symbol (ARM only).</li>
             <li>Fixed the inclusion order of <code>$(SYSROOT)/usr/include</code> for x86 builds.
-            See the <a href="http://code.google.com/p/android/issues/detail?id=18540">bug</a> for
+            See the <a href="http://b.android.com/18540">bug</a> for
             more information.</li>
             <li>Fixed the definitions of <code>ptrdiff_t</code> and <code>size_t</code> in
             x86-specific systems when they are used with the x86 standalone toolchain.</li>
diff --git a/docs/html/tools/sdk/tools-notes.jd b/docs/html/tools/sdk/tools-notes.jd
index b58fdd1..cd2d986 100644
--- a/docs/html/tools/sdk/tools-notes.jd
+++ b/docs/html/tools/sdk/tools-notes.jd
@@ -29,6 +29,42 @@
 <div class="toggle-content opened">
   <p><a href="#" onclick="return toggleContent(this)">
     <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-content-img"
+      alt=""/>SDK Tools, Revision 22.0.4</a> <em>(July 2013)</em>
+  </p>
+
+  <div class="toggle-content-toggleme">
+
+    <dl>
+    <dt>Dependencies:</dt>
+    <dd>
+      <ul>
+        <li>Android SDK Platform-tools revision 16 or later.</li>
+        <li>If you are developing in Eclipse with the
+          <a href="{@docRoot}tools/sdk/eclipse-adt.html">ADT Plugin</a>, note that this version of
+          SDK Tools is designed for use with ADT 22.0.4 and later. If you haven't already, update
+          ADT to 22.0.4.</li>
+        <li>If you are using <a href="{@docRoot}sdk/installing/studio.html">Android Studio</a>,
+          note that this version of the SDK Tools is designed to work with Android Studio
+          0.2.x and later.</li>
+        <li>If you are developing without an integrated development environment (IDE), you must have
+          <a href="http://ant.apache.org/">Apache Ant</a> 1.8 or later.</li>
+      </ul>
+    </dd>
+
+    <dt>General Notes:</dt>
+    <dd>
+      <ul>
+        <li>Fixed problem with compiling Renderscript code.</li>
+      </ul>
+    </dd>
+    </dl>
+  </div>
+</div>
+
+
+<div class="toggle-content closed">
+  <p><a href="#" onclick="return toggleContent(this)">
+    <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-content-img"
       alt=""/>SDK Tools, Revision 22.0.1</a> <em>(May 2013)</em>
   </p>
 
diff --git a/docs/html/training/implementing-navigation/nav-drawer.jd b/docs/html/training/implementing-navigation/nav-drawer.jd
index 527d570..38b73454 100644
--- a/docs/html/training/implementing-navigation/nav-drawer.jd
+++ b/docs/html/training/implementing-navigation/nav-drawer.jd
@@ -124,6 +124,7 @@
 <pre>
 public class MainActivity extends Activity {
     private String[] mPlanetTitles;
+    private DrawerLayout mDrawerLayout;
     private ListView mDrawerList;
     ...
 
@@ -133,6 +134,7 @@
         setContentView(R.layout.activity_main);
 
         mPlanetTitles = getResources().getStringArray(R.array.planets_array);
+        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
         mDrawerList = (ListView) findViewById(R.id.left_drawer);
 
         // Set the adapter for the list view
@@ -191,9 +193,9 @@
                    .commit();
 
     // Highlight the selected item, update the title, and close the drawer
-    mDrawer.setItemChecked(position, true);
+    mDrawerList.setItemChecked(position, true);
     setTitle(mPlanetTitles[position]);
-    mDrawerLayout.closeDrawer(mDrawer);
+    mDrawerLayout.closeDrawer(mDrawerList);
 }
 
 &#64;Override
diff --git a/docs/html/training/index.jd b/docs/html/training/index.jd
index 72ad018..82fbd16 100644
--- a/docs/html/training/index.jd
+++ b/docs/html/training/index.jd
@@ -3,7 +3,14 @@
 page.metaDescription=Android Training provides a collection of classes that aim to help you build great apps for Android. Each class explains the steps required to solve a problem or implement a feature using code snippets and sample code for you to use in your apps.
 
 @jd:body
-
+<div id="butterbar-wrapper" >
+  <div id="butterbar" >
+    <div id="butterbar-message">
+<a target="_blank" href="https://docs.google.com/a/google.com/forms/d/1EHLPGqhbxj2HungHRRN4_0K9TGpc-Izy-u46vBDgS8Q/viewform">
+      Take the Android Developer Survey</a>
+    </div>
+  </div>
+</div>
 
 <p>Welcome to Training for Android developers. Here you'll find sets of lessons within classes
 that describe how to accomplish a specific task with code samples you can re-use in your app.
diff --git a/docs/html/training/sync-adapters/creating-authenticator.jd b/docs/html/training/sync-adapters/creating-authenticator.jd
index 1b272e7..dfa9027 100644
--- a/docs/html/training/sync-adapters/creating-authenticator.jd
+++ b/docs/html/training/sync-adapters/creating-authenticator.jd
@@ -235,7 +235,7 @@
 <p>
     The following snippet shows the XML file for the authenticator you created previously:
 </p>
-<pre>the
+<pre>
 &lt;?xml version="1.0" encoding="utf-8"?&gt;
 &lt;account-authenticator
         xmlns:android="http://schemas.android.com/apk/res/android"
diff --git a/docs/html/training/sync-adapters/index.jd b/docs/html/training/sync-adapters/index.jd
index 1f7977b..b107cbe 100644
--- a/docs/html/training/sync-adapters/index.jd
+++ b/docs/html/training/sync-adapters/index.jd
@@ -11,7 +11,7 @@
 
 <h2>Dependencies and prerequisites</h2>
 <ul>
-    <li>Android 3.0 (API Level 11) or higher</li>
+    <li>Android 2.1 (API Level 7) or higher</li>
 </ul>
 
 <h2>You should also read</h2>
diff --git a/graphics/java/android/graphics/Bitmap.java b/graphics/java/android/graphics/Bitmap.java
index 50231da..ad5bfc8 100644
--- a/graphics/java/android/graphics/Bitmap.java
+++ b/graphics/java/android/graphics/Bitmap.java
@@ -170,7 +170,98 @@
     public void setDensity(int density) {
         mDensity = density;
     }
-    
+
+    /**
+     * <p>Modifies the bitmap to have a specified width, height, and {@link
+     * Config}, without affecting the underlying allocation backing the bitmap.
+     * Bitmap pixel data is not re-initialized for the new configuration.</p>
+     *
+     * <p>This method can be used to avoid allocating a new bitmap, instead
+     * reusing an existing bitmap's allocation for a new configuration of equal
+     * or lesser size. If the Bitmap's allocation isn't large enough to support
+     * the new configuration, an IllegalArgumentException will be thrown and the
+     * bitmap will not be modified.</p>
+     *
+     * <p>The result of {@link #getByteCount()} will reflect the new configuration,
+     * while {@link #getAllocationByteCount()} will reflect that of the initial
+     * configuration.</p>
+     *
+     * <p>WARNING: This method should NOT be called on a bitmap currently used
+     * by the view system. It does not make guarantees about how the underlying
+     * pixel buffer is remapped to the new config, just that the allocation is
+     * reused. Additionally, the view system does not account for bitmap
+     * properties being modifying during use, e.g. while attached to
+     * drawables.</p>
+     *
+     * @see #setWidth(int)
+     * @see #setHeight(int)
+     * @see #setConfig(Config)
+     */
+    public void reconfigure(int width, int height, Config config) {
+        checkRecycled("Can't call reconfigure() on a recycled bitmap");
+        if (width <= 0 || height <= 0) {
+            throw new IllegalArgumentException("width and height must be > 0");
+        }
+        if (!isMutable()) {
+            throw new IllegalArgumentException("only mutable bitmaps may be reconfigured");
+        }
+        if (mBuffer == null) {
+            throw new IllegalArgumentException("only non-inPurgeable bitmaps may be reconfigured");
+        }
+
+        nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length);
+        mWidth = width;
+        mHeight = height;
+    }
+
+    /**
+     * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
+     * with the current height and config.</p>
+     *
+     * <p>WARNING: this method should not be used on bitmaps currently used by
+     * the view system, see {@link #reconfigure(int, int, Config)} for more
+     * details.</p>
+     *
+     * @see #reconfigure(int, int, Config)
+     * @see #setHeight(int)
+     * @see #setConfig(Config)
+     */
+    public void setWidth(int width) {
+        reconfigure(width, getHeight(), getConfig());
+    }
+
+    /**
+     * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
+     * with the current width and config.</p>
+     *
+     * <p>WARNING: this method should not be used on bitmaps currently used by
+     * the view system, see {@link #reconfigure(int, int, Config)} for more
+     * details.</p>
+     *
+     * @see #reconfigure(int, int, Config)
+     * @see #setWidth(int)
+     * @see #setConfig(Config)
+     */
+    public void setHeight(int height) {
+        reconfigure(getWidth(), height, getConfig());
+    }
+
+    /**
+     * <p>Convenience method for calling {@link #reconfigure(int, int, Config)}
+     * with the current height and width.</p>
+     *
+     * <p>WARNING: this method should not be used on bitmaps currently used by
+     * the view system, see {@link #reconfigure(int, int, Config)} for more
+     * details.</p>
+     *
+     * @see #reconfigure(int, int, Config)
+     * @see #setWidth(int)
+     * @see #setHeight(int)
+     */
+    public void setConfig(Config config) {
+        reconfigure(getWidth(), getHeight(), config);
+    }
+
     /**
      * Sets the nine patch chunk.
      *
@@ -1010,7 +1101,7 @@
      *
      * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, the result of this method can
      * no longer be used to determine memory usage of a bitmap. See {@link
-     * #getAllocationByteCount()}.
+     * #getAllocationByteCount()}.</p>
      */
     public final int getByteCount() {
         // int result permits bitmaps up to 46,340 x 46,340
@@ -1021,11 +1112,15 @@
      * Returns the size of the allocated memory used to store this bitmap's pixels.
      *
      * <p>This can be larger than the result of {@link #getByteCount()} if a bitmap is reused to
-     * decode other bitmaps of smaller size. See {@link BitmapFactory.Options#inBitmap inBitmap in
-     * BitmapFactory.Options}. If a bitmap is not reused in this way, this value will be the same as
-     * that returned by {@link #getByteCount()}.
+     * decode other bitmaps of smaller size, or by manual reconfiguration. See {@link
+     * #reconfigure(int, int, Config)}, {@link #setWidth(int)}, {@link #setHeight(int)}, {@link
+     * #setConfig(Bitmap.Config)}, and {@link BitmapFactory.Options#inBitmap
+     * BitmapFactory.Options.inBitmap}. If a bitmap is not modified in this way, this value will be
+     * the same as that returned by {@link #getByteCount()}.</p>
      *
-     * <p>This value will not change over the lifetime of a Bitmap.
+     * <p>This value will not change over the lifetime of a Bitmap.</p>
+     *
+     * @see #reconfigure(int, int, Config)
      */
     public final int getAllocationByteCount() {
         return mBuffer.length;
@@ -1423,11 +1518,13 @@
 
     private static native Bitmap nativeCreate(int[] colors, int offset,
                                               int stride, int width, int height,
-                                            int nativeConfig, boolean mutable);
+                                              int nativeConfig, boolean mutable);
     private static native Bitmap nativeCopy(int srcBitmap, int nativeConfig,
                                             boolean isMutable);
     private static native void nativeDestructor(int nativeBitmap);
     private static native boolean nativeRecycle(int nativeBitmap);
+    private static native void nativeReconfigure(int nativeBitmap, int width, int height,
+                                                 int config, int allocSize);
 
     private static native boolean nativeCompress(int nativeBitmap, int format,
                                             int quality, OutputStream stream,
diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java
index deccac1..a4124bf 100644
--- a/graphics/java/android/graphics/BitmapFactory.java
+++ b/graphics/java/android/graphics/BitmapFactory.java
@@ -18,6 +18,7 @@
 
 import android.content.res.AssetManager;
 import android.content.res.Resources;
+import android.os.Trace;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.util.TypedValue;
@@ -47,34 +48,40 @@
 
         /**
          * If set, decode methods that take the Options object will attempt to
-         * reuse this bitmap when loading content. If the decode operation cannot
-         * use this bitmap, the decode method will return <code>null</code> and
-         * will throw an IllegalArgumentException. The current implementation
-         * necessitates that the reused bitmap be mutable, and the resulting
-         * reused bitmap will continue to remain mutable even when decoding a
-         * resource which would normally result in an immutable bitmap.
+         * reuse this bitmap when loading content. If the decode operation
+         * cannot use this bitmap, the decode method will return
+         * <code>null</code> and will throw an IllegalArgumentException. The
+         * current implementation necessitates that the reused bitmap be
+         * mutable, and the resulting reused bitmap will continue to remain
+         * mutable even when decoding a resource which would normally result in
+         * an immutable bitmap.</p>
          *
-         * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, any mutable
-         * bitmap can be reused to decode any other bitmaps as long as the resulting
-         * {@link Bitmap#getByteCount() byte count} of the decoded bitmap is less
-         * than or equal to the {@link Bitmap#getAllocationByteCount() allocated byte count}
-         * of the reused bitmap. This can be because the intrinsic size is smaller,
-         * or the size after density / sampled size scaling is smaller.
+         * <p>As of {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}, any
+         * mutable bitmap can be reused to decode any other bitmaps as long as
+         * the resulting {@link Bitmap#getByteCount() byte count} of the decoded
+         * bitmap is less than or equal to the {@link
+         * Bitmap#getAllocationByteCount() allocated byte count} of the reused
+         * bitmap. This can be because the intrinsic size is smaller, or its
+         * size post scaling (for density / sample size) is smaller.</p>
          *
-         * <p>Prior to {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE} additional
-         * constraints apply: The image being decoded (whether as a resource or
-         * as a stream) must be in jpeg or png format. Only equal sized bitmaps
-         * are supported, with {@link #inSampleSize} set to 1. Additionally, the
-         * {@link android.graphics.Bitmap.Config configuration} of the reused
-         * bitmap will override the setting of {@link #inPreferredConfig}, if set.
+         * <p>Prior to {@link android.os.Build.VERSION_CODES#KEY_LIME_PIE}
+         * additional constraints apply: The image being decoded (whether as a
+         * resource or as a stream) must be in jpeg or png format. Only equal
+         * sized bitmaps are supported, with {@link #inSampleSize} set to 1.
+         * Additionally, the {@link android.graphics.Bitmap.Config
+         * configuration} of the reused bitmap will override the setting of
+         * {@link #inPreferredConfig}, if set.</p>
          *
          * <p>You should still always use the returned Bitmap of the decode
          * method and not assume that reusing the bitmap worked, due to the
          * constraints outlined above and failure situations that can occur.
          * Checking whether the return value matches the value of the inBitmap
          * set in the Options structure will indicate if the bitmap was reused,
-         * but in all cases you should use the Bitmap returned by the decoding function to ensure
-         * that you are using the bitmap that was used as the decode destination.</p>
+         * but in all cases you should use the Bitmap returned by the decoding
+         * function to ensure that you are using the bitmap that was used as the
+         * decode destination.</p>
+         *
+         * @see Bitmap#reconfigure(int,int,Config)
          */
         public Bitmap inBitmap;
 
@@ -436,12 +443,21 @@
         if ((offset | length) < 0 || data.length < offset + length) {
             throw new ArrayIndexOutOfBoundsException();
         }
-        Bitmap bm = nativeDecodeByteArray(data, offset, length, opts);
 
-        if (bm == null && opts != null && opts.inBitmap != null) {
-            throw new IllegalArgumentException("Problem decoding into existing bitmap");
+        Bitmap bm;
+
+        Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
+        try {
+            bm = nativeDecodeByteArray(data, offset, length, opts);
+
+            if (bm == null && opts != null && opts.inBitmap != null) {
+                throw new IllegalArgumentException("Problem decoding into existing bitmap");
+            }
+            setDensityFromOptions(bm, opts);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
         }
-        setDensityFromOptions(bm, opts);
+
         return bm;
     }
 
@@ -508,39 +524,43 @@
             return null;
         }
 
-        // we need mark/reset to work properly
-
-        if (!is.markSupported()) {
-            is = new BufferedInputStream(is, DECODE_BUFFER_SIZE);
-        }
-
-        // so we can call reset() if a given codec gives up after reading up to
-        // this many bytes. FIXME: need to find out from the codecs what this
-        // value should be.
-        is.mark(1024);
-
         Bitmap bm;
-        boolean finish = true;
 
-        if (is instanceof AssetManager.AssetInputStream) {
-            final int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
-            bm = nativeDecodeAsset(asset, outPadding, opts);
-        } else {
-            // pass some temp storage down to the native code. 1024 is made up,
-            // but should be large enough to avoid too many small calls back
-            // into is.read(...) This number is not related to the value passed
-            // to mark(...) above.
-            byte [] tempStorage = null;
-            if (opts != null) tempStorage = opts.inTempStorage;
-            if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
-            bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
+        Trace.traceBegin(Trace.TRACE_TAG_GRAPHICS, "decodeBitmap");
+        try {
+            // we need mark/reset to work properly
+            if (!is.markSupported()) {
+                is = new BufferedInputStream(is, DECODE_BUFFER_SIZE);
+            }
+
+            // so we can call reset() if a given codec gives up after reading up to
+            // this many bytes. FIXME: need to find out from the codecs what this
+            // value should be.
+            is.mark(1024);
+
+            if (is instanceof AssetManager.AssetInputStream) {
+                final int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
+                bm = nativeDecodeAsset(asset, outPadding, opts);
+            } else {
+                // pass some temp storage down to the native code. 1024 is made up,
+                // but should be large enough to avoid too many small calls back
+                // into is.read(...) This number is not related to the value passed
+                // to mark(...) above.
+                byte [] tempStorage = null;
+                if (opts != null) tempStorage = opts.inTempStorage;
+                if (tempStorage == null) tempStorage = new byte[DECODE_BUFFER_SIZE];
+                bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
+            }
+
+            if (bm == null && opts != null && opts.inBitmap != null) {
+                throw new IllegalArgumentException("Problem decoding into existing bitmap");
+            }
+
+            setDensityFromOptions(bm, opts);
+        } finally {
+            Trace.traceEnd(Trace.TRACE_TAG_GRAPHICS);
         }
 
-        if (bm == null && opts != null && opts.inBitmap != null) {
-            throw new IllegalArgumentException("Problem decoding into existing bitmap");
-        }
-
-        setDensityFromOptions(bm, opts);
         return bm;
     }
 
@@ -608,10 +628,7 @@
     private static native Bitmap nativeDecodeFileDescriptor(FileDescriptor fd,
             Rect padding, Options opts);
     private static native Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts);
-    private static native Bitmap nativeDecodeAsset(int asset, Rect padding, Options opts,
-            boolean applyScale, float scale);
     private static native Bitmap nativeDecodeByteArray(byte[] data, int offset,
             int length, Options opts);
-    private static native byte[] nativeScaleNinePatch(byte[] chunk, float scale, Rect pad);
     private static native boolean nativeIsSeekable(FileDescriptor fd);
 }
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/graphics/java/android/graphics/Matrix.java b/graphics/java/android/graphics/Matrix.java
index a837294..4e06448 100644
--- a/graphics/java/android/graphics/Matrix.java
+++ b/graphics/java/android/graphics/Matrix.java
@@ -267,13 +267,23 @@
             native_set(native_instance, src.native_instance);
         }
     }
-    
+
     /** Returns true iff obj is a Matrix and its values equal our values.
     */
+    @Override
     public boolean equals(Object obj) {
-        return obj != null &&
-               obj instanceof Matrix &&
-               native_equals(native_instance, ((Matrix)obj).native_instance);
+        //if (obj == this) return true;     -- NaN value would mean matrix != itself
+        if (!(obj instanceof Matrix)) return false;
+        return native_equals(native_instance, ((Matrix)obj).native_instance);
+    }
+
+    @Override
+    public int hashCode() {
+        // This should generate the hash code by performing some arithmetic operation on all
+        // the matrix elements -- our equals() does an element-by-element comparison, and we
+        // need to ensure that the hash code for two equal objects is the same.  We're not
+        // really using this at the moment, so we take the easy way out.
+        return 44;
     }
 
     /** Set the matrix to identity */
@@ -512,7 +522,7 @@
          */
         END     (3);
 
-        // the native values must match those in SkMatrix.h 
+        // the native values must match those in SkMatrix.h
         ScaleToFit(int nativeInt) {
             this.nativeInt = nativeInt;
         }
@@ -535,7 +545,7 @@
         }
         return native_setRectToRect(native_instance, src, dst, stf.nativeInt);
     }
-    
+
     // private helper to perform range checks on arrays of "points"
     private static void checkPointArrays(float[] src, int srcIndex,
                                          float[] dst, int dstIndex,
@@ -598,7 +608,7 @@
         native_mapPoints(native_instance, dst, dstIndex, src, srcIndex,
                          pointCount, true);
     }
-    
+
     /**
     * Apply this matrix to the array of 2D vectors specified by src, and write
      * the transformed vectors into the array of vectors specified by dst. The
@@ -620,7 +630,7 @@
         native_mapPoints(native_instance, dst, dstIndex, src, srcIndex,
                          vectorCount, false);
     }
-    
+
     /**
      * Apply this matrix to the array of 2D points specified by src, and write
      * the transformed points into the array of points specified by dst. The
@@ -713,7 +723,7 @@
     public float mapRadius(float radius) {
         return native_mapRadius(native_instance, radius);
     }
-    
+
     /** Copy 9 values from the matrix into the array.
     */
     public void getValues(float[] values) {
@@ -736,13 +746,14 @@
         native_setValues(native_instance, values);
     }
 
+    @Override
     public String toString() {
         StringBuilder sb = new StringBuilder(64);
         sb.append("Matrix{");
         toShortString(sb);
         sb.append('}');
         return sb.toString();
-                
+
     }
 
     public String toShortString() {
@@ -780,13 +791,18 @@
                 pw.print(values[5]); pw.print("][");
         pw.print(values[6]); pw.print(", "); pw.print(values[7]); pw.print(", ");
                 pw.print(values[8]); pw.print(']');
-                
+
     }
 
+    @Override
     protected void finalize() throws Throwable {
-        finalizer(native_instance);
+        try {
+            finalizer(native_instance);
+        } finally {
+            super.finalize();
+        }
     }
-    
+
     /*package*/ final int ni() {
         return native_instance;
     }
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index af1a447..aaed094 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -95,21 +95,6 @@
      * @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
      */
     public SurfaceTexture(int texName) {
-        this(texName, false);
-    }
-
-    /**
-     * Construct a new SurfaceTexture to stream images to a given OpenGL texture.
-     *
-     * @param texName the OpenGL texture object name (e.g. generated via glGenTextures)
-     * @param allowSynchronousMode whether the SurfaceTexture can run in the synchronous mode.
-     *      When the image stream comes from OpenGL, SurfaceTexture may run in the synchronous
-     *      mode where the producer side may be blocked to avoid skipping frames. To avoid the
-     *      thread block, set allowSynchronousMode to false.
-     *
-     * @hide
-     */
-    public SurfaceTexture(int texName, boolean allowSynchronousMode) {
         Looper looper;
         if ((looper = Looper.myLooper()) != null) {
             mEventHandler = new EventHandler(looper);
@@ -118,7 +103,7 @@
         } else {
             mEventHandler = null;
         }
-        nativeInit(texName, new WeakReference<SurfaceTexture>(this), allowSynchronousMode);
+        nativeInit(texName, new WeakReference<SurfaceTexture>(this));
     }
 
     /**
@@ -299,7 +284,7 @@
         }
     }
 
-    private native void nativeInit(int texName, Object weakSelf, boolean allowSynchronousMode);
+    private native void nativeInit(int texName, Object weakSelf);
     private native void nativeFinalize();
     private native void nativeGetTransformMatrix(float[] mtx);
     private native long nativeGetTimestamp();
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/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 73082c1..bd371a3 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -31,7 +31,6 @@
 Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight):
         caches(Caches::getInstance()), texture(caches) {
     mesh = NULL;
-    meshIndices = NULL;
     meshElementCount = 0;
     cacheable = true;
     dirty = false;
@@ -57,7 +56,6 @@
     deleteTexture();
 
     delete[] mesh;
-    delete[] meshIndices;
     delete deferredList;
 }
 
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index ebd5543..b70042f 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -277,7 +277,6 @@
      * If the layer can be rendered as a mesh, this is non-null.
      */
     TextureVertex* mesh;
-    uint16_t* meshIndices;
     GLsizei meshElementCount;
 
     /**
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index cfb1e97e..f8076cc 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -130,10 +130,7 @@
     if (mLayer->region.isRect() || mLayer->region.isEmpty()) {
         if (mLayer->mesh) {
             delete[] mLayer->mesh;
-            delete[] mLayer->meshIndices;
-
             mLayer->mesh = NULL;
-            mLayer->meshIndices = NULL;
             mLayer->meshElementCount = 0;
         }
 
@@ -154,17 +151,11 @@
 
     if (mLayer->mesh && mLayer->meshElementCount < elementCount) {
         delete[] mLayer->mesh;
-        delete[] mLayer->meshIndices;
-
         mLayer->mesh = NULL;
-        mLayer->meshIndices = NULL;
     }
 
-    bool rebuildIndices = false;
     if (!mLayer->mesh) {
         mLayer->mesh = new TextureVertex[count * 4];
-        mLayer->meshIndices = new uint16_t[elementCount];
-        rebuildIndices = true;
     }
     mLayer->meshElementCount = elementCount;
 
@@ -173,7 +164,6 @@
     const float height = mLayer->layer.getHeight();
 
     TextureVertex* mesh = mLayer->mesh;
-    uint16_t* indices = mLayer->meshIndices;
 
     for (size_t i = 0; i < count; i++) {
         const android::Rect* r = &rects[i];
@@ -187,17 +177,6 @@
         TextureVertex::set(mesh++, r->right, r->top, u2, v1);
         TextureVertex::set(mesh++, r->left, r->bottom, u1, v2);
         TextureVertex::set(mesh++, r->right, r->bottom, u2, v2);
-
-        if (rebuildIndices) {
-            uint16_t quad = i * 4;
-            int index = i * 6;
-            indices[index    ] = quad;       // top-left
-            indices[index + 1] = quad + 1;   // top-right
-            indices[index + 2] = quad + 2;   // bottom-left
-            indices[index + 3] = quad + 2;   // bottom-left
-            indices[index + 4] = quad + 1;   // top-right
-            indices[index + 5] = quad + 3;   // bottom-right
-        }
     }
 }
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 7c0f3ad..06315ba 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -107,6 +107,15 @@
 };
 
 ///////////////////////////////////////////////////////////////////////////////
+// Functions
+///////////////////////////////////////////////////////////////////////////////
+
+template<typename T>
+static inline T min(T a, T b) {
+    return a < b ? a : b;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
@@ -445,6 +454,7 @@
     mCaches.enableScissor();
     if (mDirtyClip) {
         setScissorFromClip();
+        setStencilFromClip();
     }
 
     Rect clip(*mSnapshot->clipRect);
@@ -1283,7 +1293,6 @@
 
 void OpenGLRenderer::drawRegionRects(const SkRegion& region, int color,
         SkXfermode::Mode mode, bool dirty) {
-    int count = 0;
     Vector<float> rects;
 
     SkRegion::Iterator it(region);
@@ -1293,11 +1302,10 @@
         rects.push(r.fTop);
         rects.push(r.fRight);
         rects.push(r.fBottom);
-        count += 4;
         it.next();
     }
 
-    drawColorRects(rects.array(), count, color, mode, true, dirty, false);
+    drawColorRects(rects.array(), rects.size(), color, mode, true, dirty, false);
 }
 
 void OpenGLRenderer::dirtyLayer(const float left, const float top,
@@ -1327,6 +1335,21 @@
     }
 }
 
+void OpenGLRenderer::drawIndexedQuads(Vertex* mesh, GLsizei quadsCount) {
+    GLsizei elementsCount = quadsCount * 6;
+    while (elementsCount > 0) {
+        GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
+
+        setupDrawIndexedVertices(&mesh[0].position[0]);
+        glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL);
+
+        elementsCount -= drawCount;
+        // Though there are 4 vertices in a quad, we use 6 indices per
+        // quad to draw with GL_TRIANGLES
+        mesh += (drawCount / 6) * 4;
+    }
+}
+
 void OpenGLRenderer::clearLayerRegions() {
     const size_t count = mLayers.size();
     if (count == 0) return;
@@ -1341,17 +1364,15 @@
         // is likely different so we need to disable clipping here
         bool scissorChanged = mCaches.disableScissor();
 
-        Vertex mesh[count * 6];
+        Vertex mesh[count * 4];
         Vertex* vertex = mesh;
 
         for (uint32_t i = 0; i < count; i++) {
             Rect* bounds = mLayers.itemAt(i);
 
-            Vertex::set(vertex++, bounds->left, bounds->bottom);
             Vertex::set(vertex++, bounds->left, bounds->top);
             Vertex::set(vertex++, bounds->right, bounds->top);
             Vertex::set(vertex++, bounds->left, bounds->bottom);
-            Vertex::set(vertex++, bounds->right, bounds->top);
             Vertex::set(vertex++, bounds->right, bounds->bottom);
 
             delete bounds;
@@ -1367,9 +1388,8 @@
         setupDrawProgram();
         setupDrawPureColorUniforms();
         setupDrawModelViewTranslate(0.0f, 0.0f, 0.0f, 0.0f, true);
-        setupDrawVertices(&mesh[0].position[0]);
 
-        glDrawArrays(GL_TRIANGLES, 0, count * 6);
+        drawIndexedQuads(&mesh[0], count);
 
         if (scissorChanged) mCaches.enableScissor();
     } else {
@@ -1975,10 +1995,10 @@
     }
 }
 
-void OpenGLRenderer::setupDrawVertices(GLvoid* vertices) {
+void OpenGLRenderer::setupDrawIndexedVertices(GLvoid* vertices) {
     bool force = mCaches.unbindMeshBuffer();
+    mCaches.bindIndicesBuffer();
     mCaches.bindPositionVertexPointer(force, vertices, gVertexStride);
-    mCaches.unbindIndicesBuffer();
 }
 
 void OpenGLRenderer::finishDrawTexture() {
@@ -3138,11 +3158,22 @@
                 setupDrawModelViewTranslate(x, y,
                         x + layer->layer.getWidth(), y + layer->layer.getHeight());
             }
-            setupDrawMesh(&layer->mesh[0].position[0], &layer->mesh[0].texture[0]);
 
-            DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
-                    glDrawElements(GL_TRIANGLES, layer->meshElementCount,
-                            GL_UNSIGNED_SHORT, layer->meshIndices));
+            TextureVertex* mesh = &layer->mesh[0];
+            GLsizei elementsCount = layer->meshElementCount;
+
+            while (elementsCount > 0) {
+                GLsizei drawCount = min(elementsCount, (GLsizei) gMaxNumberOfQuads * 6);
+
+                setupDrawMeshIndices(&mesh[0].position[0], &mesh[0].texture[0]);
+                DRAW_DOUBLE_STENCIL_IF(!layer->hasDrawnSinceUpdate,
+                        glDrawElements(GL_TRIANGLES, drawCount, GL_UNSIGNED_SHORT, NULL));
+
+                elementsCount -= drawCount;
+                // Though there are 4 vertices in a quad, we use 6 indices per
+                // quad to draw with GL_TRIANGLES
+                mesh += (drawCount / 6) * 4;
+            }
 
             finishDrawTexture();
 
@@ -3360,8 +3391,7 @@
     float right = FLT_MIN;
     float bottom = FLT_MIN;
 
-    int vertexCount = 0;
-    Vertex mesh[count * 6];
+    Vertex mesh[count];
     Vertex* vertex = mesh;
 
     for (int index = 0; index < count; index += 4) {
@@ -3370,15 +3400,11 @@
         float r = rects[index + 2];
         float b = rects[index + 3];
 
-        Vertex::set(vertex++, l, b);
         Vertex::set(vertex++, l, t);
         Vertex::set(vertex++, r, t);
         Vertex::set(vertex++, l, b);
-        Vertex::set(vertex++, r, t);
         Vertex::set(vertex++, r, b);
 
-        vertexCount += 6;
-
         left = fminf(left, l);
         top = fminf(top, t);
         right = fmaxf(right, r);
@@ -3401,13 +3427,12 @@
     setupDrawColorUniforms();
     setupDrawShaderUniforms();
     setupDrawColorFilterUniforms();
-    setupDrawVertices((GLvoid*) &mesh[0].position[0]);
 
     if (dirty && hasLayer()) {
         dirtyLayer(left, top, right, bottom, currentTransform());
     }
 
-    glDrawArrays(GL_TRIANGLES, 0, vertexCount);
+    drawIndexedQuads(&mesh[0], count / 4);
 
     return DrawGlInfo::kStatusDrew;
 }
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 5e731b4..eb42540 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -850,6 +850,13 @@
             bool ignoreTransform, bool ignoreScale = false, bool dirty = true);
 
     /**
+     * Draws the specified list of vertices as quads using indexed GL_TRIANGLES.
+     * If the number of vertices to draw exceeds the number of indices we have
+     * pre-allocated, this method will generate several glDrawElements() calls.
+     */
+    void drawIndexedQuads(Vertex* mesh, GLsizei quadsCount);
+
+    /**
      * Draws text underline and strike-through if needed.
      *
      * @param text The text to decor
@@ -988,7 +995,7 @@
     void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords = NULL, GLuint vbo = 0);
     void setupDrawMesh(GLvoid* vertices, GLvoid* texCoords, GLvoid* colors);
     void setupDrawMeshIndices(GLvoid* vertices, GLvoid* texCoords, GLuint vbo = 0);
-    void setupDrawVertices(GLvoid* vertices);
+    void setupDrawIndexedVertices(GLvoid* vertices);
     void finishDrawTexture();
     void accountForClear(SkXfermode::Mode mode);
 
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 3144e8a7..bcd0398 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1659,10 +1659,16 @@
      * @see #playSoundEffect(int)
      */
     public static final int FX_KEYPRESS_RETURN = 8;
+
+    /**
+     * Invalid keypress sound
+     * @see #playSoundEffect(int)
+     */
+    public static final int FX_KEYPRESS_INVALID = 9;
     /**
      * @hide Number of sound effects
      */
-    public static final int NUM_SOUND_EFFECTS = 9;
+    public static final int NUM_SOUND_EFFECTS = 10;
 
     /**
      * Plays a sound effect (Key clicks, lid open/close...)
@@ -1676,6 +1682,7 @@
      *            {@link #FX_KEYPRESS_SPACEBAR},
      *            {@link #FX_KEYPRESS_DELETE},
      *            {@link #FX_KEYPRESS_RETURN},
+     *            {@link #FX_KEYPRESS_INVALID},
      * NOTE: This version uses the UI settings to determine
      * whether sounds are heard or not.
      */
@@ -1708,6 +1715,7 @@
      *            {@link #FX_KEYPRESS_SPACEBAR},
      *            {@link #FX_KEYPRESS_DELETE},
      *            {@link #FX_KEYPRESS_RETURN},
+     *            {@link #FX_KEYPRESS_INVALID},
      * @param volume Sound effect volume.
      * The volume value is a raw scalar so UI controls should be scaled logarithmically.
      * If a volume of -1 is specified, the AudioManager.STREAM_MUSIC stream volume minus 3dB will be used.
@@ -1988,7 +1996,7 @@
         IAudioService service = getService();
         try {
             service.requestAudioFocus(streamType, durationHint, mICallBack, null,
-                    AudioService.IN_VOICE_COMM_FOCUS_ID,
+                    MediaFocusControl.IN_VOICE_COMM_FOCUS_ID,
                     mContext.getBasePackageName());
         } catch (RemoteException e) {
             Log.e(TAG, "Can't call requestAudioFocusForCall() on AudioService due to "+e);
@@ -2004,7 +2012,7 @@
     public void abandonAudioFocusForCall() {
         IAudioService service = getService();
         try {
-            service.abandonAudioFocus(null, AudioService.IN_VOICE_COMM_FOCUS_ID);
+            service.abandonAudioFocus(null, MediaFocusControl.IN_VOICE_COMM_FOCUS_ID);
         } catch (RemoteException e) {
             Log.e(TAG, "Can't call abandonAudioFocusForCall() on AudioService due to "+e);
         }
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 9e13ca4..c178ae4 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -22,12 +22,12 @@
 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
 
 import android.app.Activity;
+import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
 import android.app.AppOpsManager;
 import android.app.KeyguardManager;
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
-import android.app.PendingIntent.OnFinished;
 import android.bluetooth.BluetoothA2dp;
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
@@ -106,7 +106,7 @@
  *
  * @hide
  */
-public class AudioService extends IAudioService.Stub implements OnFinished {
+public class AudioService extends IAudioService.Stub {
 
     private static final String TAG = "AudioService";
 
@@ -145,34 +145,25 @@
     private static final int MSG_BTA2DP_DOCK_TIMEOUT = 7;
     private static final int MSG_LOAD_SOUND_EFFECTS = 8;
     private static final int MSG_SET_FORCE_USE = 9;
-    private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 10;
-    private static final int MSG_BT_HEADSET_CNCT_FAILED = 11;
-    private static final int MSG_RCDISPLAY_CLEAR = 12;
-    private static final int MSG_RCDISPLAY_UPDATE = 13;
-    private static final int MSG_SET_ALL_VOLUMES = 14;
-    private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
-    private static final int MSG_REPORT_NEW_ROUTES = 16;
-    private static final int MSG_REEVALUATE_REMOTE = 17;
-    private static final int MSG_RCC_NEW_PLAYBACK_INFO = 18;
-    private static final int MSG_RCC_NEW_VOLUME_OBS = 19;
-    private static final int MSG_SET_FORCE_BT_A2DP_USE = 20;
+    private static final int MSG_BT_HEADSET_CNCT_FAILED = 10;
+    private static final int MSG_SET_ALL_VOLUMES = 11;
+    private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 12;
+    private static final int MSG_REPORT_NEW_ROUTES = 13;
+    private static final int MSG_SET_FORCE_BT_A2DP_USE = 14;
+    private static final int MSG_SET_RSX_CONNECTION_STATE = 15; // change remote submix connection
+    private static final int MSG_CHECK_MUSIC_ACTIVE = 16;
+    private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 17;
+    private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = 18;
+    private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED = 19;
+    private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 20;
+    private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 21;
+    private static final int MSG_UNLOAD_SOUND_EFFECTS = 22;
     // start of messages handled under wakelock
     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
-    private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 21;
-    private static final int MSG_SET_A2DP_CONNECTION_STATE = 22;
+    private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 100;
+    private static final int MSG_SET_A2DP_CONNECTION_STATE = 101;
     // end of messages handled under wakelock
-    private static final int MSG_SET_RSX_CONNECTION_STATE = 23; // change remote submix connection
-    private static final int MSG_CHECK_MUSIC_ACTIVE = 24;
-    private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 25;
-    private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = 26;
-    private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED = 27;
-    private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 28;
-    private static final int MSG_PROMOTE_RCC = 29;
-    private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 30;
-    private static final int MSG_UNLOAD_SOUND_EFFECTS = 31;
-    private static final int MSG_RCC_NEW_PLAYBACK_STATE = 32;
-    private static final int MSG_RCC_SEEK_REQUEST = 33;
 
     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
     // Timeout for connection to bluetooth headset service
@@ -186,7 +177,7 @@
     private VolumeStreamState[] mStreamStates;
     private SettingsObserver mSettingsObserver;
 
-    private int mMode;
+    private int mMode = AudioSystem.MODE_NORMAL;
     // protects mRingerMode
     private final Object mSettingsLock = new Object();
 
@@ -213,7 +204,7 @@
     private final int[][] SOUND_EFFECT_FILES_MAP = new int[AudioManager.NUM_SOUND_EFFECTS][2];
 
    /** @hide Maximum volume index values for audio streams */
-    private final int[] MAX_STREAM_VOLUME = new int[] {
+    private static final int[] MAX_STREAM_VOLUME = new int[] {
         5,  // STREAM_VOICE_CALL
         7,  // STREAM_SYSTEM
         7,  // STREAM_RING
@@ -345,9 +336,6 @@
     // Broadcast receiver for device connections intent broadcasts
     private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
 
-    // Used to alter media button redirection when the phone is ringing.
-    private boolean mIsRinging = false;
-
     // Devices currently connected
     private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
 
@@ -468,6 +456,10 @@
     // and used later when/if disableSafeMediaVolume() is called.
     private StreamVolumeCommand mPendingVolumeCommand;
 
+    private PowerManager.WakeLock mAudioEventWakeLock;
+
+    private final MediaFocusControl mMediaFocusControl;
+
     ///////////////////////////////////////////////////////////////////////////
     // Construction
     ///////////////////////////////////////////////////////////////////////////
@@ -481,7 +473,7 @@
                 com.android.internal.R.bool.config_voice_capable);
 
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
-        mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
+        mAudioEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleAudioEvent");
 
         Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
         mHasVibrator = vibrator == null ? false : vibrator.hasVibrator();
@@ -495,11 +487,13 @@
                 com.android.internal.R.integer.config_soundEffectVolumeDb);
 
         mVolumePanel = new VolumePanel(context, this);
-        mMode = AudioSystem.MODE_NORMAL;
         mForcedUseForComm = AudioSystem.FORCE_NONE;
 
         createAudioSystemThread();
 
+        mMediaFocusControl = new MediaFocusControl(mAudioHandler.getLooper(),
+                mContext, /*VolumeController*/ mVolumePanel, this);
+
         boolean cameraSoundForced = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_camera_sound_forced);
         mCameraSoundForced = new Boolean(cameraSoundForced);
@@ -528,6 +522,7 @@
         updateStreamVolumeAlias(false /*updateVolumes*/);
         createStreamStates();
 
+        readAndSetLowRamDevice();
         mMediaServerOk = true;
 
         // Call setRingerModeInt() to apply correct mute
@@ -568,20 +563,6 @@
 
         context.registerReceiver(mReceiver, intentFilter);
 
-        // Register for package removal intent broadcasts for media button receiver persistence
-        IntentFilter pkgFilter = new IntentFilter();
-        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
-        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
-        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
-        pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
-        pkgFilter.addDataScheme("package");
-        context.registerReceiver(mReceiver, pkgFilter);
-
-        // Register for phone state monitoring
-        TelephonyManager tmgr = (TelephonyManager)
-                context.getSystemService(Context.TELEPHONY_SERVICE);
-        tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
-
         mUseMasterVolume = context.getResources().getBoolean(
                 com.android.internal.R.bool.config_useMasterVolume);
         restoreMasterVolume();
@@ -589,11 +570,6 @@
         mMasterVolumeRamp = context.getResources().getIntArray(
                 com.android.internal.R.array.config_masterVolumeRamp);
 
-        mMainRemote = new RemotePlaybackState(-1, MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC],
-                MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
-        mHasRemotePlayback = false;
-        mMainRemoteIsActive = false;
-        postReevaluateRemote();
     }
 
     private void createAudioSystemThread() {
@@ -795,7 +771,7 @@
         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
 
         // Restore the default media button receiver from the system settings
-        restoreMediaButtonReceiver();
+        mMediaFocusControl.restoreMediaButtonReceiver();
     }
 
     private int rescaleIndex(int index, int srcStream, int dstStream) {
@@ -817,8 +793,8 @@
     public void adjustLocalOrRemoteStreamVolume(int streamType, int direction,
             String callingPackage) {
         if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
-        if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
-            adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
+        if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
+            mMediaFocusControl.adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
         } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
             adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0, callingPackage);
         }
@@ -847,7 +823,7 @@
             // don't play sounds for remote
             flags &= ~(AudioManager.FLAG_PLAY_SOUND|AudioManager.FLAG_FIXED_VOLUME);
             //if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()");
-            adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
+            mMediaFocusControl.adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
         } else {
             adjustStreamVolume(streamType, direction, flags, callingPackage);
         }
@@ -1274,6 +1250,10 @@
         return AudioSystem.getMasterMute();
     }
 
+    protected static int getMaxStreamVolume(int streamType) {
+        return MAX_STREAM_VOLUME[streamType];
+    }
+
     /** @see AudioManager#getStreamVolume(int) */
     public int getStreamVolume(int streamType) {
         ensureValidStreamType(streamType);
@@ -2597,7 +2577,7 @@
             } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
                 // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
                 // volume can have priority over STREAM_MUSIC
-                if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
+                if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
                     if (DEBUG_VOL)
                         Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
                     return STREAM_REMOTE_MUSIC;
@@ -2637,7 +2617,7 @@
                 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
                 return AudioSystem.STREAM_NOTIFICATION;
             } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
-                if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
+                if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
                     // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
                     // volume can have priority over STREAM_MUSIC
                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
@@ -2681,7 +2661,7 @@
      */
     private void queueMsgUnderWakeLock(Handler handler, int msg,
             int arg1, int arg2, Object obj, int delay) {
-        mMediaEventWakeLock.acquire();
+        mAudioEventWakeLock.acquire();
         sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
     }
 
@@ -3379,13 +3359,6 @@
             }
         }
 
-        private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
-            Settings.System.putStringForUser(mContentResolver,
-                                             Settings.System.MEDIA_BUTTON_RECEIVER,
-                                             receiver == null ? "" : receiver.flattenToString(),
-                                             UserHandle.USER_CURRENT);
-        }
-
         private void cleanupPlayer(MediaPlayer mp) {
             if (mp != null) {
                 try {
@@ -3469,6 +3442,8 @@
                     // process restarts after a crash, not the first time it is started.
                     AudioSystem.setParameters("restarting=true");
 
+                    readAndSetLowRamDevice();
+
                     // Restore device connection states
                     synchronized (mConnectedDevices) {
                         Set set = mConnectedDevices.entrySet();
@@ -3562,31 +3537,18 @@
                     setForceUse(msg.arg1, msg.arg2);
                     break;
 
-                case MSG_PERSIST_MEDIABUTTONRECEIVER:
-                    onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
-                    break;
-
-                case MSG_RCDISPLAY_CLEAR:
-                    onRcDisplayClear();
-                    break;
-
-                case MSG_RCDISPLAY_UPDATE:
-                    // msg.obj is guaranteed to be non null
-                    onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
-                    break;
-
                 case MSG_BT_HEADSET_CNCT_FAILED:
                     resetBluetoothSco();
                     break;
 
                 case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
                     onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
-                    mMediaEventWakeLock.release();
+                    mAudioEventWakeLock.release();
                     break;
 
                 case MSG_SET_A2DP_CONNECTION_STATE:
                     onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
-                    mMediaEventWakeLock.release();
+                    mAudioEventWakeLock.release();
                     break;
 
                 case MSG_REPORT_NEW_ROUTES: {
@@ -3609,26 +3571,6 @@
                     break;
                 }
 
-                case MSG_REEVALUATE_REMOTE:
-                    onReevaluateRemote();
-                    break;
-
-                case MSG_RCC_NEW_PLAYBACK_INFO:
-                    onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
-                            ((Integer)msg.obj).intValue() /* value */);
-                    break;
-                case MSG_RCC_NEW_VOLUME_OBS:
-                    onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
-                            (IRemoteVolumeObserver)msg.obj /* rvo */);
-                    break;
-                case MSG_RCC_NEW_PLAYBACK_STATE:
-                    onNewPlaybackStateForRcc(msg.arg1 /* rccId */, msg.arg2 /* state */,
-                            (RccPlaybackState)msg.obj /* newState */);
-                    break;
-                case MSG_RCC_SEEK_REQUEST:
-                    onSetRemoteControlClientPlaybackPosition(msg.arg1 /* generationId */,
-                            ((Long)msg.obj).longValue() /* timeMs */);
-
                 case MSG_SET_RSX_CONNECTION_STATE:
                     onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
                     break;
@@ -3649,10 +3591,6 @@
                     onPersistSafeVolumeState(msg.arg1);
                     break;
 
-                case MSG_PROMOTE_RCC:
-                    onPromoteRcc(msg.arg1);
-                    break;
-
                 case MSG_BROADCAST_BT_CONNECTION_STATE:
                     onBroadcastScoConnectionState(msg.arg1);
                     break;
@@ -4130,21 +4068,6 @@
                         0,
                         null,
                         SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
-            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
-                    || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
-                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
-                    // a package is being removed, not replaced
-                    String packageName = intent.getData().getSchemeSpecificPart();
-                    if (packageName != null) {
-                        cleanupMediaButtonReceiverForPackage(packageName, true);
-                    }
-                }
-            } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)
-                    || action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
-                String packageName = intent.getData().getSchemeSpecificPart();
-                if (packageName != null) {
-                    cleanupMediaButtonReceiverForPackage(packageName, false);
-                }
             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
                 AudioSystem.setParameters("screen_state=on");
             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
@@ -4161,7 +4084,7 @@
                         null,
                         0);
                 // the current audio focus owner is no longer valid
-                discardAudioFocusOwner();
+                mMediaFocusControl.discardAudioFocusOwner();
 
                 // load volume settings for new user
                 readAudioSettings(true /*userSwitch*/);
@@ -4177,2213 +4100,102 @@
     }
 
     //==========================================================================================
-    // AudioFocus
+    // RemoteControlDisplay / RemoteControlClient / Remote info
     //==========================================================================================
-
-    /* constant to identify focus stack entry that is used to hold the focus while the phone
-     * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
-     * entering and exiting calls.
-     */
-    public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
-
-    private final static Object mAudioFocusLock = new Object();
-
-    private final static Object mRingingLock = new Object();
-
-    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
-        @Override
-        public void onCallStateChanged(int state, String incomingNumber) {
-            if (state == TelephonyManager.CALL_STATE_RINGING) {
-                //Log.v(TAG, " CALL_STATE_RINGING");
-                synchronized(mRingingLock) {
-                    mIsRinging = true;
-                }
-            } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
-                    || (state == TelephonyManager.CALL_STATE_IDLE)) {
-                synchronized(mRingingLock) {
-                    mIsRinging = false;
-                }
-            }
-        }
-    };
-
-    /**
-     * Discard the current audio focus owner.
-     * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
-     * focus), remove it from the stack, and clear the remote control display.
-     */
-    private void discardAudioFocusOwner() {
-        synchronized(mAudioFocusLock) {
-            if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
-                // notify the current focus owner it lost focus after removing it from stack
-                FocusStackEntry focusOwner = mFocusStack.pop();
-                try {
-                    focusOwner.mFocusDispatcher.dispatchAudioFocusChange(
-                            AudioManager.AUDIOFOCUS_LOSS, focusOwner.mClientId);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failure to signal loss of audio focus due to "+ e);
-                    e.printStackTrace();
-                }
-                focusOwner.unlinkToDeath();
-                // clear RCD
-                synchronized(mRCStack) {
-                    clearRemoteControlDisplay_syncAfRcs();
-                }
-            }
-        }
-    }
-
-    private void notifyTopOfAudioFocusStack() {
-        // notify the top of the stack it gained focus
-        if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
-            if (canReassignAudioFocus()) {
-                try {
-                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
-                            AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
-                } catch (RemoteException e) {
-                    Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
-                    e.printStackTrace();
-                }
-            }
-        }
-    }
-
-    private static class FocusStackEntry {
-        public int mStreamType = -1;// no stream type
-        public IAudioFocusDispatcher mFocusDispatcher = null;
-        public IBinder mSourceRef = null;
-        public String mClientId;
-        public int mFocusChangeType;
-        public AudioFocusDeathHandler mHandler;
-        public String mPackageName;
-        public int mCallingUid;
-
-        public FocusStackEntry() {
-        }
-
-        public FocusStackEntry(int streamType, int duration,
-                IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
-                String pn, int uid) {
-            mStreamType = streamType;
-            mFocusDispatcher = afl;
-            mSourceRef = source;
-            mClientId = id;
-            mFocusChangeType = duration;
-            mHandler = hdlr;
-            mPackageName = pn;
-            mCallingUid = uid;
-        }
-
-        public void unlinkToDeath() {
-            try {
-                if (mSourceRef != null && mHandler != null) {
-                    mSourceRef.unlinkToDeath(mHandler, 0);
-                    mHandler = null;
-                }
-            } catch (java.util.NoSuchElementException e) {
-                Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
-            }
-        }
-
-        @Override
-        protected void finalize() throws Throwable {
-            unlinkToDeath(); // unlink exception handled inside method
-            super.finalize();
-        }
-    }
-
-    private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
-
-    /**
-     * Helper function:
-     * Display in the log the current entries in the audio focus stack
-     */
-    private void dumpFocusStack(PrintWriter pw) {
-        pw.println("\nAudio Focus stack entries (last is top of stack):");
-        synchronized(mAudioFocusLock) {
-            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
-            while(stackIterator.hasNext()) {
-                FocusStackEntry fse = stackIterator.next();
-                pw.println("  source:" + fse.mSourceRef
-                        + " -- pack: " + fse.mPackageName
-                        + " -- client: " + fse.mClientId
-                        + " -- duration: " + fse.mFocusChangeType
-                        + " -- uid: " + fse.mCallingUid
-                        + " -- stream: " + fse.mStreamType);
-            }
-        }
-    }
-
-    /**
-     * Helper function:
-     * Called synchronized on mAudioFocusLock
-     * Remove a focus listener from the focus stack.
-     * @param clientToRemove the focus listener
-     * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
-     *   focus, notify the next item in the stack it gained focus.
-     */
-    private void removeFocusStackEntry(String clientToRemove, boolean signal) {
-        // is the current top of the focus stack abandoning focus? (because of request, not death)
-        if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
-        {
-            //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
-            FocusStackEntry fse = mFocusStack.pop();
-            fse.unlinkToDeath();
-            if (signal) {
-                // notify the new top of the stack it gained focus
-                notifyTopOfAudioFocusStack();
-                // there's a new top of the stack, let the remote control know
-                synchronized(mRCStack) {
-                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-                }
-            }
-        } else {
-            // focus is abandoned by a client that's not at the top of the stack,
-            // no need to update focus.
-            // (using an iterator on the stack so we can safely remove an entry after having
-            //  evaluated it, traversal order doesn't matter here)
-            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
-            while(stackIterator.hasNext()) {
-                FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
-                if(fse.mClientId.equals(clientToRemove)) {
-                    Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
-                            + fse.mClientId);
-                    stackIterator.remove();
-                    fse.unlinkToDeath();
-                }
-            }
-        }
-    }
-
-    /**
-     * Helper function:
-     * Called synchronized on mAudioFocusLock
-     * Remove focus listeners from the focus stack for a particular client when it has died.
-     */
-    private void removeFocusStackEntryForClient(IBinder cb) {
-        // is the owner of the audio focus part of the client to remove?
-        boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
-                mFocusStack.peek().mSourceRef.equals(cb);
-        // (using an iterator on the stack so we can safely remove an entry after having
-        //  evaluated it, traversal order doesn't matter here)
-        Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
-        while(stackIterator.hasNext()) {
-            FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
-            if(fse.mSourceRef.equals(cb)) {
-                Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
-                        + fse.mClientId);
-                stackIterator.remove();
-                // the client just died, no need to unlink to its death
-            }
-        }
-        if (isTopOfStackForClientToRemove) {
-            // we removed an entry at the top of the stack:
-            //  notify the new top of the stack it gained focus.
-            notifyTopOfAudioFocusStack();
-            // there's a new top of the stack, let the remote control know
-            synchronized(mRCStack) {
-                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-            }
-        }
-    }
-
-    /**
-     * Helper function:
-     * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
-     */
-    private boolean canReassignAudioFocus() {
-        // focus requests are rejected during a phone call or when the phone is ringing
-        // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
-        if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * Inner class to monitor audio focus client deaths, and remove them from the audio focus
-     * stack if necessary.
-     */
-    private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
-        private IBinder mCb; // To be notified of client's death
-
-        AudioFocusDeathHandler(IBinder cb) {
-            mCb = cb;
-        }
-
-        public void binderDied() {
-            synchronized(mAudioFocusLock) {
-                Log.w(TAG, "  AudioFocus   audio focus client died");
-                removeFocusStackEntryForClient(mCb);
-            }
-        }
-
-        public IBinder getBinder() {
-            return mCb;
-        }
-    }
-
-
-    /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int)  */
-    public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
-            IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
-        Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
-        // the main stream type for the audio focus request is currently not used. It may
-        // potentially be used to handle multiple stream type-dependent audio focuses.
-
-        // we need a valid binder callback for clients
-        if (!cb.pingBinder()) {
-            Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
-            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
-        }
-
-        if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
-                callingPackageName) != AppOpsManager.MODE_ALLOWED) {
-            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
-        }
-
-        synchronized(mAudioFocusLock) {
-            if (!canReassignAudioFocus()) {
-                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
-            }
-
-            // handle the potential premature death of the new holder of the focus
-            // (premature death == death before abandoning focus)
-            // Register for client death notification
-            AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
-            try {
-                cb.linkToDeath(afdh, 0);
-            } catch (RemoteException e) {
-                // client has already died!
-                Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
-                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
-            }
-
-            if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
-                // if focus is already owned by this client and the reason for acquiring the focus
-                // hasn't changed, don't do anything
-                if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
-                    // unlink death handler so it can be gc'ed.
-                    // linkToDeath() creates a JNI global reference preventing collection.
-                    cb.unlinkToDeath(afdh, 0);
-                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
-                }
-                // the reason for the audio focus request has changed: remove the current top of
-                // stack and respond as if we had a new focus owner
-                FocusStackEntry fse = mFocusStack.pop();
-                fse.unlinkToDeath();
-            }
-
-            // notify current top of stack it is losing focus
-            if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
-                try {
-                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
-                            -1 * focusChangeHint, // loss and gain codes are inverse of each other
-                            mFocusStack.peek().mClientId);
-                } catch (RemoteException e) {
-                    Log.e(TAG, " Failure to signal loss of focus due to "+ e);
-                    e.printStackTrace();
-                }
-            }
-
-            // focus requester might already be somewhere below in the stack, remove it
-            removeFocusStackEntry(clientId, false /* signal */);
-
-            // push focus requester at the top of the audio focus stack
-            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
-                    clientId, afdh, callingPackageName, Binder.getCallingUid()));
-
-            // there's a new top of the stack, let the remote control know
-            synchronized(mRCStack) {
-                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-            }
-        }//synchronized(mAudioFocusLock)
-
-        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
-    }
-
-    /** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener)  */
-    public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
-        Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
-        try {
-            // this will take care of notifying the new focus owner if needed
-            synchronized(mAudioFocusLock) {
-                removeFocusStackEntry(clientId, true);
-            }
-        } catch (java.util.ConcurrentModificationException cme) {
-            // Catching this exception here is temporary. It is here just to prevent
-            // a crash seen when the "Silent" notification is played. This is believed to be fixed
-            // but this try catch block is left just to be safe.
-            Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
-            cme.printStackTrace();
-        }
-
-        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
-    }
-
-
-    public void unregisterAudioFocusClient(String clientId) {
-        synchronized(mAudioFocusLock) {
-            removeFocusStackEntry(clientId, false);
-        }
-    }
-
-
-    //==========================================================================================
-    // RemoteControl
-    //==========================================================================================
-    public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
-        filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
-    }
-
-    public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
-        filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
-    }
-
-    private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
-        // sanity check on the incoming key event
-        if (!isValidMediaKeyEvent(keyEvent)) {
-            Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
-            return;
-        }
-        // event filtering for telephony
-        synchronized(mRingingLock) {
-            synchronized(mRCStack) {
-                if ((mMediaReceiverForCalls != null) &&
-                        (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL))) {
-                    dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
-                    return;
-                }
-            }
-        }
-        // event filtering based on voice-based interactions
-        if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
-            filterVoiceInputKeyEvent(keyEvent, needWakeLock);
-        } else {
-            dispatchMediaKeyEvent(keyEvent, needWakeLock);
-        }
-    }
-
-    /**
-     * Handles the dispatching of the media button events to the telephony package.
-     * Precondition: mMediaReceiverForCalls != null
-     * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
-     * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
-     *     is dispatched.
-     */
-    private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
-        Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
-        keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-        keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
-        if (needWakeLock) {
-            mMediaEventWakeLock.acquire();
-            keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
-        }
-        final long ident = Binder.clearCallingIdentity();
-        try {
-            mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
-                    null, mKeyEventDone, mAudioHandler, Activity.RESULT_OK, null, null);
-        } finally {
-            Binder.restoreCallingIdentity(ident);
-        }
-    }
-
-    /**
-     * Handles the dispatching of the media button events to one of the registered listeners,
-     * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
-     * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
-     * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
-     *     is dispatched.
-     */
-    private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
-        if (needWakeLock) {
-            mMediaEventWakeLock.acquire();
-        }
-        Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
-        keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
-        synchronized(mRCStack) {
-            if (!mRCStack.empty()) {
-                // send the intent that was registered by the client
-                try {
-                    mRCStack.peek().mMediaIntent.send(mContext,
-                            needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
-                            keyIntent, AudioService.this, mAudioHandler);
-                } catch (CanceledException e) {
-                    Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
-                    e.printStackTrace();
-                }
-            } else {
-                // legacy behavior when nobody registered their media button event receiver
-                //    through AudioManager
-                if (needWakeLock) {
-                    keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
-                }
-                final long ident = Binder.clearCallingIdentity();
-                try {
-                    mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
-                            null, mKeyEventDone,
-                            mAudioHandler, Activity.RESULT_OK, null, null);
-                } finally {
-                    Binder.restoreCallingIdentity(ident);
-                }
-            }
-        }
-    }
-
-    /**
-     * The different actions performed in response to a voice button key event.
-     */
-    private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
-    private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
-    private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
-
-    private final Object mVoiceEventLock = new Object();
-    private boolean mVoiceButtonDown;
-    private boolean mVoiceButtonHandled;
-
-    /**
-     * Filter key events that may be used for voice-based interactions
-     * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
-     *    media buttons that can be used to trigger voice-based interactions.
-     * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
-     *     is dispatched.
-     */
-    private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
-        if (DEBUG_RC) {
-            Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
-        }
-
-        int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
-        int keyAction = keyEvent.getAction();
-        synchronized (mVoiceEventLock) {
-            if (keyAction == KeyEvent.ACTION_DOWN) {
-                if (keyEvent.getRepeatCount() == 0) {
-                    // initial down
-                    mVoiceButtonDown = true;
-                    mVoiceButtonHandled = false;
-                } else if (mVoiceButtonDown && !mVoiceButtonHandled
-                        && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
-                    // long-press, start voice-based interactions
-                    mVoiceButtonHandled = true;
-                    voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
-                }
-            } else if (keyAction == KeyEvent.ACTION_UP) {
-                if (mVoiceButtonDown) {
-                    // voice button up
-                    mVoiceButtonDown = false;
-                    if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
-                        voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
-                    }
-                }
-            }
-        }//synchronized (mVoiceEventLock)
-
-        // take action after media button event filtering for voice-based interactions
-        switch (voiceButtonAction) {
-            case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
-                if (DEBUG_RC) Log.v(TAG, "   ignore key event");
-                break;
-            case VOICEBUTTON_ACTION_START_VOICE_INPUT:
-                if (DEBUG_RC) Log.v(TAG, "   start voice-based interactions");
-                // then start the voice-based interactions
-                startVoiceBasedInteractions(needWakeLock);
-                break;
-            case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
-                if (DEBUG_RC) Log.v(TAG, "   send simulated key event, wakelock=" + needWakeLock);
-                sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
-                break;
-        }
-    }
-
-    private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
-        // send DOWN event
-        KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
-        dispatchMediaKeyEvent(keyEvent, needWakeLock);
-        // send UP event
-        keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
-        dispatchMediaKeyEvent(keyEvent, needWakeLock);
-
-    }
-
-
-    private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
-        if (keyEvent == null) {
-            return false;
-        }
-        final int keyCode = keyEvent.getKeyCode();
-        switch (keyCode) {
-            case KeyEvent.KEYCODE_MUTE:
-            case KeyEvent.KEYCODE_HEADSETHOOK:
-            case KeyEvent.KEYCODE_MEDIA_PLAY:
-            case KeyEvent.KEYCODE_MEDIA_PAUSE:
-            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
-            case KeyEvent.KEYCODE_MEDIA_STOP:
-            case KeyEvent.KEYCODE_MEDIA_NEXT:
-            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
-            case KeyEvent.KEYCODE_MEDIA_REWIND:
-            case KeyEvent.KEYCODE_MEDIA_RECORD:
-            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
-            case KeyEvent.KEYCODE_MEDIA_CLOSE:
-            case KeyEvent.KEYCODE_MEDIA_EJECT:
-                break;
-            default:
-                return false;
-        }
-        return true;
-    }
-
-    /**
-     * Checks whether the given key code is one that can trigger the launch of voice-based
-     *   interactions.
-     * @param keyCode the key code associated with the key event
-     * @return true if the key is one of the supported voice-based interaction triggers
-     */
-    private static boolean isValidVoiceInputKeyCode(int keyCode) {
-        if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
-            return true;
-        } else {
-            return false;
-        }
-    }
-
-    /**
-     * Tell the system to start voice-based interactions / voice commands
-     */
-    private void startVoiceBasedInteractions(boolean needWakeLock) {
-        Intent voiceIntent = null;
-        // select which type of search to launch:
-        // - screen on and device unlocked: action is ACTION_WEB_SEARCH
-        // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
-        //    with EXTRA_SECURE set to true if the device is securely locked
-        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
-        boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
-        if (!isLocked && pm.isScreenOn()) {
-            voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
-            Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
-        } else {
-            voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
-            voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
-                    isLocked && mKeyguardManager.isKeyguardSecure());
-            Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
-        }
-        // start the search activity
-        if (needWakeLock) {
-            mMediaEventWakeLock.acquire();
-        }
-        try {
-            if (voiceIntent != null) {
-                voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
-                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-                mContext.startActivity(voiceIntent);
-            }
-        } catch (ActivityNotFoundException e) {
-            Log.w(TAG, "No activity for search: " + e);
-        } finally {
-            if (needWakeLock) {
-                mMediaEventWakeLock.release();
-            }
-        }
-    }
-
-    private PowerManager.WakeLock mMediaEventWakeLock;
-
-    private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
-
-    // only set when wakelock was acquired, no need to check value when received
-    private static final String EXTRA_WAKELOCK_ACQUIRED =
-            "android.media.AudioService.WAKELOCK_ACQUIRED";
-
-    public void onSendFinished(PendingIntent pendingIntent, Intent intent,
-            int resultCode, String resultData, Bundle resultExtras) {
-        if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
-            mMediaEventWakeLock.release();
-        }
-    }
-
-    BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
-        public void onReceive(Context context, Intent intent) {
-            if (intent == null) {
-                return;
-            }
-            Bundle extras = intent.getExtras();
-            if (extras == null) {
-                return;
-            }
-            if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
-                mMediaEventWakeLock.release();
-            }
-        }
-    };
-
-    /**
-     * Synchronization on mCurrentRcLock always inside a block synchronized on mRCStack
-     */
-    private final Object mCurrentRcLock = new Object();
-    /**
-     * The one remote control client which will receive a request for display information.
-     * This object may be null.
-     * Access protected by mCurrentRcLock.
-     */
-    private IRemoteControlClient mCurrentRcClient = null;
-
-    private final static int RC_INFO_NONE = 0;
-    private final static int RC_INFO_ALL =
-        RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
-        RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
-        RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
-        RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
-
-    /**
-     * A monotonically increasing generation counter for mCurrentRcClient.
-     * Only accessed with a lock on mCurrentRcLock.
-     * No value wrap-around issues as we only act on equal values.
-     */
-    private int mCurrentRcClientGen = 0;
-
-    /**
-     * Inner class to monitor remote control client deaths, and remove the client for the
-     * remote control stack if necessary.
-     */
-    private class RcClientDeathHandler implements IBinder.DeathRecipient {
-        final private IBinder mCb; // To be notified of client's death
-        final private PendingIntent mMediaIntent;
-
-        RcClientDeathHandler(IBinder cb, PendingIntent pi) {
-            mCb = cb;
-            mMediaIntent = pi;
-        }
-
-        public void binderDied() {
-            Log.w(TAG, "  RemoteControlClient died");
-            // remote control client died, make sure the displays don't use it anymore
-            //  by setting its remote control client to null
-            registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
-            // the dead client was maybe handling remote playback, reevaluate
-            postReevaluateRemote();
-        }
-
-        public IBinder getBinder() {
-            return mCb;
-        }
-    }
-
-    /**
-     * A global counter for RemoteControlClient identifiers
-     */
-    private static int sLastRccId = 0;
-
-    private class RemotePlaybackState {
-        int mRccId;
-        int mVolume;
-        int mVolumeMax;
-        int mVolumeHandling;
-
-        private RemotePlaybackState(int id, int vol, int volMax) {
-            mRccId = id;
-            mVolume = vol;
-            mVolumeMax = volMax;
-            mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
-        }
-    }
-
-    /**
-     * Internal cache for the playback information of the RemoteControlClient whose volume gets to
-     * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
-     * every time we need this info.
-     */
-    private RemotePlaybackState mMainRemote;
-    /**
-     * Indicates whether the "main" RemoteControlClient is considered active.
-     * Use synchronized on mMainRemote.
-     */
-    private boolean mMainRemoteIsActive;
-    /**
-     * Indicates whether there is remote playback going on. True even if there is no "active"
-     * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
-     * handles remote playback.
-     * Use synchronized on mMainRemote.
-     */
-    private boolean mHasRemotePlayback;
-
-    private static class RccPlaybackState {
-        public int mState;
-        public long mPositionMs;
-        public float mSpeed;
-
-        public RccPlaybackState(int state, long positionMs, float speed) {
-            mState = state;
-            mPositionMs = positionMs;
-            mSpeed = speed;
-        }
-
-        public void reset() {
-            mState = RemoteControlClient.PLAYSTATE_STOPPED;
-            mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID;
-            mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X;
-        }
-
-        @Override
-        public String toString() {
-            return stateToString() + ", "
-                    + ((mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) ?
-                            "PLAYBACK_POSITION_INVALID ," : String.valueOf(mPositionMs)) + "ms ,"
-                    + mSpeed + "X";
-        }
-
-        private String stateToString() {
-            switch (mState) {
-                case RemoteControlClient.PLAYSTATE_NONE:
-                    return "PLAYSTATE_NONE";
-                case RemoteControlClient.PLAYSTATE_STOPPED:
-                    return "PLAYSTATE_STOPPED";
-                case RemoteControlClient.PLAYSTATE_PAUSED:
-                    return "PLAYSTATE_PAUSED";
-                case RemoteControlClient.PLAYSTATE_PLAYING:
-                    return "PLAYSTATE_PLAYING";
-                case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
-                    return "PLAYSTATE_FAST_FORWARDING";
-                case RemoteControlClient.PLAYSTATE_REWINDING:
-                    return "PLAYSTATE_REWINDING";
-                case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
-                    return "PLAYSTATE_SKIPPING_FORWARDS";
-                case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
-                    return "PLAYSTATE_SKIPPING_BACKWARDS";
-                case RemoteControlClient.PLAYSTATE_BUFFERING:
-                    return "PLAYSTATE_BUFFERING";
-                case RemoteControlClient.PLAYSTATE_ERROR:
-                    return "PLAYSTATE_ERROR";
-                default:
-                    return "[invalid playstate]";
-            }
-        }
-    }
-
-    private static class RemoteControlStackEntry implements DeathRecipient {
-        public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
-        final public AudioService mService;
-        /**
-         * The target for the ACTION_MEDIA_BUTTON events.
-         * Always non null.
-         */
-        final public PendingIntent mMediaIntent;
-        /**
-         * The registered media button event receiver.
-         * Always non null.
-         */
-        final public ComponentName mReceiverComponent;
-        public IBinder mToken;
-        public String mCallingPackageName;
-        public int mCallingUid;
-        /**
-         * Provides access to the information to display on the remote control.
-         * May be null (when a media button event receiver is registered,
-         *     but no remote control client has been registered) */
-        public IRemoteControlClient mRcClient;
-        public RcClientDeathHandler mRcClientDeathHandler;
-        /**
-         * Information only used for non-local playback
-         */
-        public int mPlaybackType;
-        public int mPlaybackVolume;
-        public int mPlaybackVolumeMax;
-        public int mPlaybackVolumeHandling;
-        public int mPlaybackStream;
-        public RccPlaybackState mPlaybackState;
-        public IRemoteVolumeObserver mRemoteVolumeObs;
-
-        public void resetPlaybackInfo() {
-            mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
-            mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
-            mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
-            mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
-            mPlaybackStream = AudioManager.STREAM_MUSIC;
-            mPlaybackState.reset();
-            mRemoteVolumeObs = null;
-        }
-
-        /** precondition: mediaIntent != null */
-        public RemoteControlStackEntry(AudioService service, PendingIntent mediaIntent,
-                ComponentName eventReceiver, IBinder token) {
-            mService = service;
-            mMediaIntent = mediaIntent;
-            mReceiverComponent = eventReceiver;
-            mToken = token;
-            mCallingUid = -1;
-            mRcClient = null;
-            mRccId = ++sLastRccId;
-            mPlaybackState = new RccPlaybackState(
-                    RemoteControlClient.PLAYSTATE_STOPPED,
-                    RemoteControlClient.PLAYBACK_POSITION_INVALID,
-                    RemoteControlClient.PLAYBACK_SPEED_1X);
-
-            resetPlaybackInfo();
-            if (mToken != null) {
-                try {
-                    mToken.linkToDeath(this, 0);
-                } catch (RemoteException e) {
-                    mService.mAudioHandler.post(new Runnable() {
-                        @Override public void run() {
-                            mService.unregisterMediaButtonIntent(mMediaIntent);
-                        }
-                    });
-                }
-            }
-        }
-
-        public void unlinkToRcClientDeath() {
-            if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
-                try {
-                    mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
-                    mRcClientDeathHandler = null;
-                } catch (java.util.NoSuchElementException e) {
-                    // not much we can do here
-                    Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
-                    e.printStackTrace();
-                }
-            }
-        }
-
-        public void destroy() {
-            unlinkToRcClientDeath();
-            if (mToken != null) {
-                mToken.unlinkToDeath(this, 0);
-                mToken = null;
-            }
-        }
-
-        @Override
-        public void binderDied() {
-            mService.unregisterMediaButtonIntent(mMediaIntent);
-        }
-
-        @Override
-        protected void finalize() throws Throwable {
-            destroy(); // unlink exception handled inside method
-            super.finalize();
-        }
-    }
-
-    /**
-     *  The stack of remote control event receivers.
-     *  Code sections and methods that modify the remote control event receiver stack are
-     *  synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
-     *  stack, audio focus or RC, can lead to a change in the remote control display
-     */
-    private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
-
-    /**
-     * The component the telephony package can register so telephony calls have priority to
-     * handle media button events
-     */
-    private ComponentName mMediaReceiverForCalls = null;
-
-    /**
-     * Helper function:
-     * Display in the log the current entries in the remote control focus stack
-     */
-    private void dumpRCStack(PrintWriter pw) {
-        pw.println("\nRemote Control stack entries (last is top of stack):");
-        synchronized(mRCStack) {
-            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-            while(stackIterator.hasNext()) {
-                RemoteControlStackEntry rcse = stackIterator.next();
-                pw.println("  pi: " + rcse.mMediaIntent +
-                        " -- pack: " + rcse.mCallingPackageName +
-                        "  -- ercvr: " + rcse.mReceiverComponent +
-                        "  -- client: " + rcse.mRcClient +
-                        "  -- uid: " + rcse.mCallingUid +
-                        "  -- type: " + rcse.mPlaybackType +
-                        "  state: " + rcse.mPlaybackState);
-            }
-        }
-    }
-
-    /**
-     * Helper function:
-     * Display in the log the current entries in the remote control stack, focusing
-     * on RemoteControlClient data
-     */
-    private void dumpRCCStack(PrintWriter pw) {
-        pw.println("\nRemote Control Client stack entries (last is top of stack):");
-        synchronized(mRCStack) {
-            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-            while(stackIterator.hasNext()) {
-                RemoteControlStackEntry rcse = stackIterator.next();
-                pw.println("  uid: " + rcse.mCallingUid +
-                        "  -- id: " + rcse.mRccId +
-                        "  -- type: " + rcse.mPlaybackType +
-                        "  -- state: " + rcse.mPlaybackState +
-                        "  -- vol handling: " + rcse.mPlaybackVolumeHandling +
-                        "  -- vol: " + rcse.mPlaybackVolume +
-                        "  -- volMax: " + rcse.mPlaybackVolumeMax +
-                        "  -- volObs: " + rcse.mRemoteVolumeObs);
-            }
-            synchronized(mCurrentRcLock) {
-                pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen);
-            }
-        }
-        synchronized (mMainRemote) {
-            pw.println("\nRemote Volume State:");
-            pw.println("  has remote: " + mHasRemotePlayback);
-            pw.println("  is remote active: " + mMainRemoteIsActive);
-            pw.println("  rccId: " + mMainRemote.mRccId);
-            pw.println("  volume handling: "
-                    + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
-                            "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
-            pw.println("  volume: " + mMainRemote.mVolume);
-            pw.println("  volume steps: " + mMainRemote.mVolumeMax);
-        }
-    }
-
-    /**
-     * Helper function:
-     * Display in the log the current entries in the list of remote control displays
-     */
-    private void dumpRCDList(PrintWriter pw) {
-        pw.println("\nRemote Control Display list entries:");
-        synchronized(mRCStack) {
-            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
-            while (displayIterator.hasNext()) {
-                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
-                pw.println("  IRCD: " + di.mRcDisplay +
-                        "  -- w:" + di.mArtworkExpectedWidth +
-                        "  -- h:" + di.mArtworkExpectedHeight+
-                        "  -- wantsPosSync:" + di.mWantsPositionSync);
-            }
-        }
-    }
-
-    /**
-     * Helper function:
-     * Remove any entry in the remote control stack that has the same package name as packageName
-     * Pre-condition: packageName != null
-     */
-    private void cleanupMediaButtonReceiverForPackage(String packageName, boolean removeAll) {
-        synchronized(mRCStack) {
-            if (mRCStack.empty()) {
-                return;
-            } else {
-                final PackageManager pm = mContext.getPackageManager();
-                RemoteControlStackEntry oldTop = mRCStack.peek();
-                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-                // iterate over the stack entries
-                // (using an iterator on the stack so we can safely remove an entry after having
-                //  evaluated it, traversal order doesn't matter here)
-                while(stackIterator.hasNext()) {
-                    RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
-                    if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
-                        // a stack entry is from the package being removed, remove it from the stack
-                        stackIterator.remove();
-                        rcse.destroy();
-                    } else if (rcse.mReceiverComponent != null) {
-                        try {
-                            // Check to see if this receiver still exists.
-                            pm.getReceiverInfo(rcse.mReceiverComponent, 0);
-                        } catch (PackageManager.NameNotFoundException e) {
-                            // Not found -- remove it!
-                            stackIterator.remove();
-                            rcse.destroy();
-                        }
-                    }
-                }
-                if (mRCStack.empty()) {
-                    // no saved media button receiver
-                    mAudioHandler.sendMessage(
-                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
-                                    null));
-                } else if (oldTop != mRCStack.peek()) {
-                    // the top of the stack has changed, save it in the system settings
-                    // by posting a message to persist it; only do this however if it has
-                    // a concrete component name (is not a transient registration)
-                    RemoteControlStackEntry rcse = mRCStack.peek();
-                    if (rcse.mReceiverComponent != null) {
-                        mAudioHandler.sendMessage(
-                                mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
-                                        rcse.mReceiverComponent));
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Helper function:
-     * Restore remote control receiver from the system settings.
-     */
-    private void restoreMediaButtonReceiver() {
-        String receiverName = Settings.System.getStringForUser(mContentResolver,
-                Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
-        if ((null != receiverName) && !receiverName.isEmpty()) {
-            ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
-            if (eventReceiver == null) {
-                // an invalid name was persisted
-                return;
-            }
-            // construct a PendingIntent targeted to the restored component name
-            // for the media button and register it
-            Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
-            //     the associated intent will be handled by the component being registered
-            mediaButtonIntent.setComponent(eventReceiver);
-            PendingIntent pi = PendingIntent.getBroadcast(mContext,
-                    0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
-            registerMediaButtonIntent(pi, eventReceiver, null);
-        }
-    }
-
-    /**
-     * Helper function:
-     * Set the new remote control receiver at the top of the RC focus stack.
-     * Called synchronized on mAudioFocusLock, then mRCStack
-     * precondition: mediaIntent != null
-     */
-    private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target,
-            IBinder token) {
-        // already at top of stack?
-        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
-            return;
-        }
-        if (mAppOps.noteOp(AppOpsManager.OP_TAKE_MEDIA_BUTTONS, Binder.getCallingUid(),
-                mediaIntent.getCreatorPackage()) != AppOpsManager.MODE_ALLOWED) {
-            return;
-        }
-        RemoteControlStackEntry rcse = null;
-        boolean wasInsideStack = false;
-        try {
-            for (int index = mRCStack.size()-1; index >= 0; index--) {
-                rcse = mRCStack.elementAt(index);
-                if(rcse.mMediaIntent.equals(mediaIntent)) {
-                    // ok to remove element while traversing the stack since we're leaving the loop
-                    mRCStack.removeElementAt(index);
-                    wasInsideStack = true;
-                    break;
-                }
-            }
-        } catch (ArrayIndexOutOfBoundsException e) {
-            // not expected to happen, indicates improper concurrent modification
-            Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
-        }
-        if (!wasInsideStack) {
-            rcse = new RemoteControlStackEntry(this, mediaIntent, target, token);
-        }
-        mRCStack.push(rcse); // rcse is never null
-
-        // post message to persist the default media button receiver
-        if (target != null) {
-            mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
-                    MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
-        }
-    }
-
-    /**
-     * Helper function:
-     * Remove the remote control receiver from the RC focus stack.
-     * Called synchronized on mAudioFocusLock, then mRCStack
-     * precondition: pi != null
-     */
-    private void removeMediaButtonReceiver_syncAfRcs(PendingIntent pi) {
-        try {
-            for (int index = mRCStack.size()-1; index >= 0; index--) {
-                final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
-                if (rcse.mMediaIntent.equals(pi)) {
-                    rcse.destroy();
-                    // ok to remove element while traversing the stack since we're leaving the loop
-                    mRCStack.removeElementAt(index);
-                    break;
-                }
-            }
-        } catch (ArrayIndexOutOfBoundsException e) {
-            // not expected to happen, indicates improper concurrent modification
-            Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
-        }
-    }
-
-    /**
-     * Helper function:
-     * Called synchronized on mRCStack
-     */
-    private boolean isCurrentRcController(PendingIntent pi) {
-        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
-            return true;
-        }
-        return false;
-    }
-
-    //==========================================================================================
-    // Remote control display / client
-    //==========================================================================================
-    /**
-     * Update the remote control displays with the new "focused" client generation
-     */
-    private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
-            PendingIntent newMediaIntent, boolean clearing) {
-        synchronized(mRCStack) {
-            if (mRcDisplays.size() > 0) {
-                final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
-                while (displayIterator.hasNext()) {
-                    final DisplayInfoForServer di = displayIterator.next();
-                    try {
-                        di.mRcDisplay.setCurrentClientId(
-                                newClientGeneration, newMediaIntent, clearing);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc()",e);
-                        di.release();
-                        displayIterator.remove();
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * Update the remote control clients with the new "focused" client generation
-     */
-    private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
-        // (using an iterator on the stack so we can safely remove an entry if needed,
-        //  traversal order doesn't matter here as we update all entries)
-        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-        while(stackIterator.hasNext()) {
-            RemoteControlStackEntry se = stackIterator.next();
-            if ((se != null) && (se.mRcClient != null)) {
-                try {
-                    se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()",e);
-                    stackIterator.remove();
-                    se.unlinkToRcClientDeath();
-                }
-            }
-        }
-    }
-
-    /**
-     * Update the displays and clients with the new "focused" client generation and name
-     * @param newClientGeneration the new generation value matching a client update
-     * @param newMediaIntent the media button event receiver associated with the client.
-     *    May be null, which implies there is no registered media button event receiver.
-     * @param clearing true if the new client generation value maps to a remote control update
-     *    where the display should be cleared.
-     */
-    private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
-            PendingIntent newMediaIntent, boolean clearing) {
-        // send the new valid client generation ID to all displays
-        setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
-        // send the new valid client generation ID to all clients
-        setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
-    }
-
-    /**
-     * Called when processing MSG_RCDISPLAY_CLEAR event
-     */
-    private void onRcDisplayClear() {
-        if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
-
-        synchronized(mRCStack) {
-            synchronized(mCurrentRcLock) {
-                mCurrentRcClientGen++;
-                // synchronously update the displays and clients with the new client generation
-                setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
-                        null /*newMediaIntent*/, true /*clearing*/);
-            }
-        }
-    }
-
-    /**
-     * Called when processing MSG_RCDISPLAY_UPDATE event
-     */
-    private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
-        synchronized(mRCStack) {
-            synchronized(mCurrentRcLock) {
-                if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
-                    if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
-
-                    mCurrentRcClientGen++;
-                    // synchronously update the displays and clients with
-                    //      the new client generation
-                    setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
-                            rcse.mMediaIntent /*newMediaIntent*/,
-                            false /*clearing*/);
-
-                    // tell the current client that it needs to send info
-                    try {
-                        mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Current valid remote client is dead: "+e);
-                        mCurrentRcClient = null;
-                    }
-                } else {
-                    // the remote control display owner has changed between the
-                    // the message to update the display was sent, and the time it
-                    // gets to be processed (now)
-                }
-            }
-        }
-    }
-
-
-    /**
-     * Helper function:
-     * Called synchronized on mRCStack
-     */
-    private void clearRemoteControlDisplay_syncAfRcs() {
-        synchronized(mCurrentRcLock) {
-            mCurrentRcClient = null;
-        }
-        // will cause onRcDisplayClear() to be called in AudioService's handler thread
-        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
-    }
-
-    /**
-     * Helper function for code readability: only to be called from
-     *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
-     *    this method.
-     * Preconditions:
-     *    - called synchronized mAudioFocusLock then on mRCStack
-     *    - mRCStack.isEmpty() is false
-     */
-    private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
-        RemoteControlStackEntry rcse = mRCStack.peek();
-        int infoFlagsAboutToBeUsed = infoChangedFlags;
-        // this is where we enforce opt-in for information display on the remote controls
-        //   with the new AudioManager.registerRemoteControlClient() API
-        if (rcse.mRcClient == null) {
-            //Log.w(TAG, "Can't update remote control display with null remote control client");
-            clearRemoteControlDisplay_syncAfRcs();
-            return;
-        }
-        synchronized(mCurrentRcLock) {
-            if (!rcse.mRcClient.equals(mCurrentRcClient)) {
-                // new RC client, assume every type of information shall be queried
-                infoFlagsAboutToBeUsed = RC_INFO_ALL;
-            }
-            mCurrentRcClient = rcse.mRcClient;
-        }
-        // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
-        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
-                infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
-    }
-
-    /**
-     * Helper function:
-     * Called synchronized on mAudioFocusLock, then mRCStack
-     * Check whether the remote control display should be updated, triggers the update if required
-     * @param infoChangedFlags the flags corresponding to the remote control client information
-     *     that has changed, if applicable (checking for the update conditions might trigger a
-     *     clear, rather than an update event).
-     */
-    private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
-        // determine whether the remote control display should be refreshed
-        // if either stack is empty, there is a mismatch, so clear the RC display
-        if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
-            clearRemoteControlDisplay_syncAfRcs();
-            return;
-        }
-
-        // determine which entry in the AudioFocus stack to consider, and compare against the
-        // top of the stack for the media button event receivers : simply using the top of the
-        // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
-        // notifications playing during music playback.
-        // Crawl the AudioFocus stack from the top until an entry is found with the following
-        // characteristics:
-        // - focus gain on STREAM_MUSIC stream
-        // - non-transient focus gain on a stream other than music
-        FocusStackEntry af = null;
-        try {
-            for (int index = mFocusStack.size()-1; index >= 0; index--) {
-                FocusStackEntry fse = mFocusStack.elementAt(index);
-                if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
-                        || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
-                    af = fse;
-                    break;
-                }
-            }
-        } catch (ArrayIndexOutOfBoundsException e) {
-            Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e);
-            af = null;
-        }
-        if (af == null) {
-            clearRemoteControlDisplay_syncAfRcs();
-            return;
-        }
-
-        // if the audio focus and RC owners belong to different packages, there is a mismatch, clear
-        if ((mRCStack.peek().mCallingPackageName != null)
-                && (af.mPackageName != null)
-                && !(mRCStack.peek().mCallingPackageName.compareTo(
-                        af.mPackageName) == 0)) {
-            clearRemoteControlDisplay_syncAfRcs();
-            return;
-        }
-        // if the audio focus didn't originate from the same Uid as the one in which the remote
-        //   control information will be retrieved, clear
-        if (mRCStack.peek().mCallingUid != af.mCallingUid) {
-            clearRemoteControlDisplay_syncAfRcs();
-            return;
-        }
-
-        // refresh conditions were verified: update the remote controls
-        // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
-        updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
-    }
-
-    /**
-     * Helper function:
-     * Post a message to asynchronously move the media button event receiver associated with the
-     * given remote control client ID to the top of the remote control stack
-     * @param rccId
-     */
-    private void postPromoteRcc(int rccId) {
-        sendMsg(mAudioHandler, MSG_PROMOTE_RCC, SENDMSG_REPLACE,
-                rccId /*arg1*/, 0, null, 0/*delay*/);
-    }
-
-    private void onPromoteRcc(int rccId) {
-        if (DEBUG_RC) { Log.d(TAG, "Promoting RCC " + rccId); }
-        synchronized(mAudioFocusLock) {
-            synchronized(mRCStack) {
-                // ignore if given RCC ID is already at top of remote control stack
-                if (!mRCStack.isEmpty() && (mRCStack.peek().mRccId == rccId)) {
-                    return;
-                }
-                int indexToPromote = -1;
-                try {
-                    for (int index = mRCStack.size()-1; index >= 0; index--) {
-                        final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
-                        if (rcse.mRccId == rccId) {
-                            indexToPromote = index;
-                            break;
-                        }
-                    }
-                    if (indexToPromote >= 0) {
-                        if (DEBUG_RC) { Log.d(TAG, "  moving RCC from index " + indexToPromote
-                                + " to " + (mRCStack.size()-1)); }
-                        final RemoteControlStackEntry rcse = mRCStack.remove(indexToPromote);
-                        mRCStack.push(rcse);
-                        // the RC stack changed, reevaluate the display
-                        checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-                    }
-                } catch (ArrayIndexOutOfBoundsException e) {
-                    // not expected to happen, indicates improper concurrent modification
-                    Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
-                }
-            }//synchronized(mRCStack)
-        }//synchronized(mAudioFocusLock)
-    }
-
-    /**
-     * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
-     * precondition: mediaIntent != null
-     */
-    public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
-            IBinder token) {
-        Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
-
-        synchronized(mAudioFocusLock) {
-            synchronized(mRCStack) {
-                pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token);
-                // new RC client, assume every type of information shall be queried
-                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-            }
-        }
-    }
-
-    /**
-     * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
-     * precondition: mediaIntent != null, eventReceiver != null
-     */
-    public void unregisterMediaButtonIntent(PendingIntent mediaIntent)
-    {
-        Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
-
-        synchronized(mAudioFocusLock) {
-            synchronized(mRCStack) {
-                boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
-                removeMediaButtonReceiver_syncAfRcs(mediaIntent);
-                if (topOfStackWillChange) {
-                    // current RC client will change, assume every type of info needs to be queried
-                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-                }
-            }
-        }
-    }
-
-    /**
-     * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
-     * precondition: c != null
-     */
-    public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
-        if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
-                != PackageManager.PERMISSION_GRANTED) {
-            Log.e(TAG, "Invalid permissions to register media button receiver for calls");
-            return;
-        }
-        synchronized(mRCStack) {
-            mMediaReceiverForCalls = c;
-        }
-    }
-
-    /**
-     * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
-     */
-    public void unregisterMediaButtonEventReceiverForCalls() {
-        if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
-                != PackageManager.PERMISSION_GRANTED) {
-            Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
-            return;
-        }
-        synchronized(mRCStack) {
-            mMediaReceiverForCalls = null;
-        }
-    }
-
-    /**
-     * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
-     * @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient
-     * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
-     *     without modifying the RC stack, but while still causing the display to refresh (will
-     *     become blank as a result of this)
-     */
-    public int registerRemoteControlClient(PendingIntent mediaIntent,
-            IRemoteControlClient rcClient, String callingPackageName) {
-        if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
-        int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
-        synchronized(mAudioFocusLock) {
-            synchronized(mRCStack) {
-                // store the new display information
-                try {
-                    for (int index = mRCStack.size()-1; index >= 0; index--) {
-                        final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
-                        if(rcse.mMediaIntent.equals(mediaIntent)) {
-                            // already had a remote control client?
-                            if (rcse.mRcClientDeathHandler != null) {
-                                // stop monitoring the old client's death
-                                rcse.unlinkToRcClientDeath();
-                            }
-                            // save the new remote control client
-                            rcse.mRcClient = rcClient;
-                            rcse.mCallingPackageName = callingPackageName;
-                            rcse.mCallingUid = Binder.getCallingUid();
-                            if (rcClient == null) {
-                                // here rcse.mRcClientDeathHandler is null;
-                                rcse.resetPlaybackInfo();
-                                break;
-                            }
-                            rccId = rcse.mRccId;
-
-                            // there is a new (non-null) client:
-                            // 1/ give the new client the displays (if any)
-                            if (mRcDisplays.size() > 0) {
-                                plugRemoteControlDisplaysIntoClient_syncRcStack(rcse.mRcClient);
-                            }
-                            // 2/ monitor the new client's death
-                            IBinder b = rcse.mRcClient.asBinder();
-                            RcClientDeathHandler rcdh =
-                                    new RcClientDeathHandler(b, rcse.mMediaIntent);
-                            try {
-                                b.linkToDeath(rcdh, 0);
-                            } catch (RemoteException e) {
-                                // remote control client is DOA, disqualify it
-                                Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
-                                rcse.mRcClient = null;
-                            }
-                            rcse.mRcClientDeathHandler = rcdh;
-                            break;
-                        }
-                    }//for
-                } catch (ArrayIndexOutOfBoundsException e) {
-                    // not expected to happen, indicates improper concurrent modification
-                    Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
-                }
-
-                // if the eventReceiver is at the top of the stack
-                // then check for potential refresh of the remote controls
-                if (isCurrentRcController(mediaIntent)) {
-                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-                }
-            }//synchronized(mRCStack)
-        }//synchronized(mAudioFocusLock)
-        return rccId;
-    }
-
-    /**
-     * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
-     * rcClient is guaranteed non-null
-     */
-    public void unregisterRemoteControlClient(PendingIntent mediaIntent,
-            IRemoteControlClient rcClient) {
-        if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient);
-        synchronized(mAudioFocusLock) {
-            synchronized(mRCStack) {
-                boolean topRccChange = false;
-                try {
-                    for (int index = mRCStack.size()-1; index >= 0; index--) {
-                        final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
-                        if ((rcse.mMediaIntent.equals(mediaIntent))
-                                && rcClient.equals(rcse.mRcClient)) {
-                            // we found the IRemoteControlClient to unregister
-                            // stop monitoring its death
-                            rcse.unlinkToRcClientDeath();
-                            // reset the client-related fields
-                            rcse.mRcClient = null;
-                            rcse.mCallingPackageName = null;
-                            topRccChange = (index == mRCStack.size()-1);
-                            // there can only be one matching RCC in the RC stack, we're done
-                            break;
-                        }
-                    }
-                } catch (ArrayIndexOutOfBoundsException e) {
-                    // not expected to happen, indicates improper concurrent modification
-                    Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
-                }
-                if (topRccChange) {
-                    // no more RCC for the RCD, check for potential refresh of the remote controls
-                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-                }
-            }
-        }
-    }
-
-
-    /**
-     * A class to encapsulate all the information about a remote control display.
-     * After instanciation, init() must always be called before the object is added in the list
-     * of displays.
-     * Before being removed from the list of displays, release() must always be called (otherwise
-     * it will leak death handlers).
-     */
-    private class DisplayInfoForServer implements IBinder.DeathRecipient {
-        /** may never be null */
-        private IRemoteControlDisplay mRcDisplay;
-        private IBinder mRcDisplayBinder;
-        private int mArtworkExpectedWidth = -1;
-        private int mArtworkExpectedHeight = -1;
-        private boolean mWantsPositionSync = false;
-
-        public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {
-            if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h);
-            mRcDisplay = rcd;
-            mRcDisplayBinder = rcd.asBinder();
-            mArtworkExpectedWidth = w;
-            mArtworkExpectedHeight = h;
-        }
-
-        public boolean init() {
-            try {
-                mRcDisplayBinder.linkToDeath(this, 0);
-            } catch (RemoteException e) {
-                // remote control display is DOA, disqualify it
-                Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + mRcDisplayBinder);
-                return false;
-            }
-            return true;
-        }
-
-        public void release() {
-            try {
-                mRcDisplayBinder.unlinkToDeath(this, 0);
-            } catch (java.util.NoSuchElementException e) {
-                // not much we can do here, the display should have been unregistered anyway
-                Log.e(TAG, "Error in DisplaInfoForServer.relase()", e);
-            }
-        }
-
-        public void binderDied() {
-            synchronized(mRCStack) {
-                Log.w(TAG, "RemoteControl: display " + mRcDisplay + " died");
-                // remove the display from the list
-                final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
-                while (displayIterator.hasNext()) {
-                    final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
-                    if (di.mRcDisplay == mRcDisplay) {
-                        if (DEBUG_RC) Log.w(TAG, " RCD removed from list");
-                        displayIterator.remove();
-                        return;
-                    }
-                }
-            }
-        }
-    }
-
-    /**
-     * The remote control displays.
-     * Access synchronized on mRCStack
-     */
-    private ArrayList<DisplayInfoForServer> mRcDisplays = new ArrayList<DisplayInfoForServer>(1);
-
-    /**
-     * Plug each registered display into the specified client
-     * @param rcc, guaranteed non null
-     */
-    private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) {
-        final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
-        while (displayIterator.hasNext()) {
-            final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
-            try {
-                rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth,
-                        di.mArtworkExpectedHeight);
-                if (di.mWantsPositionSync) {
-                    rcc.setWantsSyncForDisplay(di.mRcDisplay, true);
-                }
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
-            }
-        }
-    }
-
-    /**
-     * Is the remote control display interface already registered
-     * @param rcd
-     * @return true if the IRemoteControlDisplay is already in the list of displays
-     */
-    private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) {
-        final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
-        while (displayIterator.hasNext()) {
-            final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
-            if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Register an IRemoteControlDisplay.
-     * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
-     * at the top of the stack to update the new display with its information.
-     * @see android.media.IAudioService#registerRemoteControlDisplay(android.media.IRemoteControlDisplay, int, int)
-     * @param rcd the IRemoteControlDisplay to register. No effect if null.
-     * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
-     *   display doesn't need to receive artwork.
-     * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
-     *   display doesn't need to receive artwork.
-     */
     public void registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
-        if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
-        synchronized(mAudioFocusLock) {
-            synchronized(mRCStack) {
-                if ((rcd == null) || rcDisplayIsPluggedIn_syncRcStack(rcd)) {
-                    return;
-                }
-                DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h);
-                if (!di.init()) {
-                    if (DEBUG_RC) Log.e(TAG, " error registering RCD");
-                    return;
-                }
-                // add RCD to list of displays
-                mRcDisplays.add(di);
-
-                // let all the remote control clients know there is a new display (so the remote
-                //   control stack traversal order doesn't matter).
-                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-                while(stackIterator.hasNext()) {
-                    RemoteControlStackEntry rcse = stackIterator.next();
-                    if(rcse.mRcClient != null) {
-                        try {
-                            rcse.mRcClient.plugRemoteControlDisplay(rcd, w, h);
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Error connecting RCD to client: ", e);
-                        }
-                    }
-                }
-
-                // we have a new display, of which all the clients are now aware: have it be updated
-                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
-            }
-        }
+        mMediaFocusControl.registerRemoteControlDisplay(rcd, w, h);
     }
 
-    /**
-     * Unregister an IRemoteControlDisplay.
-     * No effect if the IRemoteControlDisplay hasn't been successfully registered.
-     * @see android.media.IAudioService#unregisterRemoteControlDisplay(android.media.IRemoteControlDisplay)
-     * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
-     */
     public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
-        if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
-        synchronized(mRCStack) {
-            if (rcd == null) {
-                return;
-            }
-
-            boolean displayWasPluggedIn = false;
-            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
-            while (displayIterator.hasNext() && !displayWasPluggedIn) {
-                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
-                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
-                    displayWasPluggedIn = true;
-                    di.release();
-                    displayIterator.remove();
-                }
-            }
-
-            if (displayWasPluggedIn) {
-                // disconnect this remote control display from all the clients, so the remote
-                //   control stack traversal order doesn't matter
-                final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-                while(stackIterator.hasNext()) {
-                    final RemoteControlStackEntry rcse = stackIterator.next();
-                    if(rcse.mRcClient != null) {
-                        try {
-                            rcse.mRcClient.unplugRemoteControlDisplay(rcd);
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Error disconnecting remote control display to client: ", e);
-                        }
-                    }
-                }
-            } else {
-                if (DEBUG_RC) Log.w(TAG, "  trying to unregister unregistered RCD");
-            }
-        }
+        mMediaFocusControl.unregisterRemoteControlDisplay(rcd);
     }
 
-    /**
-     * Update the size of the artwork used by an IRemoteControlDisplay.
-     * @see android.media.IAudioService#remoteControlDisplayUsesBitmapSize(android.media.IRemoteControlDisplay, int, int)
-     * @param rcd the IRemoteControlDisplay with the new artwork size requirement
-     * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
-     *   display doesn't need to receive artwork.
-     * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
-     *   display doesn't need to receive artwork.
-     */
     public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
-        synchronized(mRCStack) {
-            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
-            boolean artworkSizeUpdate = false;
-            while (displayIterator.hasNext() && !artworkSizeUpdate) {
-                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
-                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
-                    if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
-                        di.mArtworkExpectedWidth = w;
-                        di.mArtworkExpectedHeight = h;
-                        artworkSizeUpdate = true;
-                    }
-                }
-            }
-            if (artworkSizeUpdate) {
-                // RCD is currently plugged in and its artwork size has changed, notify all RCCs,
-                // stack traversal order doesn't matter
-                final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-                while(stackIterator.hasNext()) {
-                    final RemoteControlStackEntry rcse = stackIterator.next();
-                    if(rcse.mRcClient != null) {
-                        try {
-                            rcse.mRcClient.setBitmapSizeForDisplay(rcd, w, h);
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "Error setting bitmap size for RCD on RCC: ", e);
-                        }
-                    }
-                }
-            }
-        }
+        mMediaFocusControl.remoteControlDisplayUsesBitmapSize(rcd, w, h);
     }
 
-    /**
-     * Controls whether a remote control display needs periodic checks of the RemoteControlClient
-     * playback position to verify that the estimated position has not drifted from the actual
-     * position. By default the check is not performed.
-     * The IRemoteControlDisplay must have been previously registered for this to have any effect.
-     * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
-     *     or disabled. Not null.
-     * @param wantsSync if true, RemoteControlClient instances which expose their playback position
-     *     to the framework will regularly compare the estimated playback position with the actual
-     *     position, and will update the IRemoteControlDisplay implementation whenever a drift is
-     *     detected.
-     */
     public void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
             boolean wantsSync) {
-        synchronized(mRCStack) {
-            boolean rcdRegistered = false;
-            // store the information about this display
-            // (display stack traversal order doesn't matter).
-            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
-            while (displayIterator.hasNext()) {
-                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
-                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
-                    di.mWantsPositionSync = wantsSync;
-                    rcdRegistered = true;
-                    break;
-                }
-            }
-            if (!rcdRegistered) {
-                return;
-            }
-            // notify all current RemoteControlClients
-            // (stack traversal order doesn't matter as we notify all RCCs)
-            final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-            while (stackIterator.hasNext()) {
-                final RemoteControlStackEntry rcse = stackIterator.next();
-                if (rcse.mRcClient != null) {
-                    try {
-                        rcse.mRcClient.setWantsSyncForDisplay(rcd, wantsSync);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error setting position sync flag for RCD on RCC: ", e);
-                    }
-                }
-            }
-        }
+        mMediaFocusControl.remoteControlDisplayWantsPlaybackPositionSync(rcd, wantsSync);
+    }
+
+    public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
+        mMediaFocusControl.registerMediaButtonEventReceiverForCalls(c);
+    }
+
+    public void unregisterMediaButtonEventReceiverForCalls() {
+        mMediaFocusControl.unregisterMediaButtonEventReceiverForCalls();
+    }
+
+    public void registerMediaButtonIntent(PendingIntent pi, ComponentName c, IBinder token) {
+        mMediaFocusControl.registerMediaButtonIntent(pi, c, token);
+    }
+
+    public void unregisterMediaButtonIntent(PendingIntent pi) {
+        mMediaFocusControl.unregisterMediaButtonIntent(pi);
+    }
+ 
+    public int registerRemoteControlClient(PendingIntent mediaIntent,
+            IRemoteControlClient rcClient, String callingPckg) {
+        return mMediaFocusControl.registerRemoteControlClient(mediaIntent, rcClient, callingPckg);
+    }
+
+    public void unregisterRemoteControlClient(PendingIntent mediaIntent,
+            IRemoteControlClient rcClient) {
+        mMediaFocusControl.unregisterRemoteControlClient(mediaIntent, rcClient);
     }
 
     public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
-        // ignore position change requests if invalid generation ID
-        synchronized(mRCStack) {
-            synchronized(mCurrentRcLock) {
-                if (mCurrentRcClientGen != generationId) {
-                    return;
-                }
-            }
-        }
-        // discard any unprocessed seek request in the message queue, and replace with latest
-        sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
-                0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
-    }
-
-    public void onSetRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
-        if(DEBUG_RC) Log.d(TAG, "onSetRemoteControlClientPlaybackPosition(genId=" + generationId +
-                ", timeMs=" + timeMs + ")");
-        synchronized(mRCStack) {
-            synchronized(mCurrentRcLock) {
-                if ((mCurrentRcClient != null) && (mCurrentRcClientGen == generationId)) {
-                    // tell the current client to seek to the requested location
-                    try {
-                        mCurrentRcClient.seekTo(generationId, timeMs);
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Current valid remote client is dead: "+e);
-                        mCurrentRcClient = null;
-                    }
-                }
-            }
-        }
-    }
-
-    public void setPlaybackInfoForRcc(int rccId, int what, int value) {
-        sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE,
-                rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */);
-    }
-
-    // handler for MSG_RCC_NEW_PLAYBACK_INFO
-    private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
-        if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
-                ", what=" + key + ",val=" + value + ")");
-        synchronized(mRCStack) {
-            // iterating from top of stack as playback information changes are more likely
-            //   on entries at the top of the remote control stack
-            try {
-                for (int index = mRCStack.size()-1; index >= 0; index--) {
-                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
-                    if (rcse.mRccId == rccId) {
-                        switch (key) {
-                            case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
-                                rcse.mPlaybackType = value;
-                                postReevaluateRemote();
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_VOLUME:
-                                rcse.mPlaybackVolume = value;
-                                synchronized (mMainRemote) {
-                                    if (rccId == mMainRemote.mRccId) {
-                                        mMainRemote.mVolume = value;
-                                        mVolumePanel.postHasNewRemotePlaybackInfo();
-                                    }
-                                }
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
-                                rcse.mPlaybackVolumeMax = value;
-                                synchronized (mMainRemote) {
-                                    if (rccId == mMainRemote.mRccId) {
-                                        mMainRemote.mVolumeMax = value;
-                                        mVolumePanel.postHasNewRemotePlaybackInfo();
-                                    }
-                                }
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
-                                rcse.mPlaybackVolumeHandling = value;
-                                synchronized (mMainRemote) {
-                                    if (rccId == mMainRemote.mRccId) {
-                                        mMainRemote.mVolumeHandling = value;
-                                        mVolumePanel.postHasNewRemotePlaybackInfo();
-                                    }
-                                }
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
-                                rcse.mPlaybackStream = value;
-                                break;
-                            default:
-                                Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
-                                break;
-                        }
-                        return;
-                    }
-                }//for
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // not expected to happen, indicates improper concurrent modification
-                Log.e(TAG, "Wrong index mRCStack on onNewPlaybackInfoForRcc, lock error? ", e);
-            }
-        }
-    }
-
-    public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
-        sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE,
-                rccId /* arg1 */, state /* arg2 */,
-                new RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
-    }
-
-    public void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) {
-        if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
-                + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
-        synchronized(mRCStack) {
-            // iterating from top of stack as playback information changes are more likely
-            //   on entries at the top of the remote control stack
-            try {
-                for (int index = mRCStack.size()-1; index >= 0; index--) {
-                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
-                    if (rcse.mRccId == rccId) {
-                        rcse.mPlaybackState = newState;
-                        synchronized (mMainRemote) {
-                            if (rccId == mMainRemote.mRccId) {
-                                mMainRemoteIsActive = isPlaystateActive(state);
-                                postReevaluateRemote();
-                            }
-                        }
-                        // an RCC moving to a "playing" state should become the media button
-                        //   event receiver so it can be controlled, without requiring the
-                        //   app to re-register its receiver
-                        if (isPlaystateActive(state)) {
-                            postPromoteRcc(rccId);
-                        }
-                    }
-                }//for
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // not expected to happen, indicates improper concurrent modification
-                Log.e(TAG, "Wrong index on mRCStack in onNewPlaybackStateForRcc, lock error? ", e);
-            }
-        }
+        mMediaFocusControl.setRemoteControlClientPlaybackPosition(generationId, timeMs);
     }
 
     public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
-        sendMsg(mAudioHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
-                rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
-    }
-
-    // handler for MSG_RCC_NEW_VOLUME_OBS
-    private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
-        synchronized(mRCStack) {
-            // The stack traversal order doesn't matter because there is only one stack entry
-            //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
-            //  start iterating from the top.
-            try {
-                for (int index = mRCStack.size()-1; index >= 0; index--) {
-                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
-                    if (rcse.mRccId == rccId) {
-                        rcse.mRemoteVolumeObs = rvo;
-                        break;
-                    }
-                }
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // not expected to happen, indicates improper concurrent modification
-                Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
-            }
-        }
-    }
-
-    /**
-     * Checks if a remote client is active on the supplied stream type. Update the remote stream
-     * volume state if found and playing
-     * @param streamType
-     * @return false if no remote playing is currently playing
-     */
-    private boolean checkUpdateRemoteStateIfActive(int streamType) {
-        synchronized(mRCStack) {
-            // iterating from top of stack as active playback is more likely on entries at the top
-            try {
-                for (int index = mRCStack.size()-1; index >= 0; index--) {
-                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
-                    if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
-                            && isPlaystateActive(rcse.mPlaybackState.mState)
-                            && (rcse.mPlaybackStream == streamType)) {
-                        if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
-                                + ", vol =" + rcse.mPlaybackVolume);
-                        synchronized (mMainRemote) {
-                            mMainRemote.mRccId = rcse.mRccId;
-                            mMainRemote.mVolume = rcse.mPlaybackVolume;
-                            mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax;
-                            mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling;
-                            mMainRemoteIsActive = true;
-                        }
-                        return true;
-                    }
-                }
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // not expected to happen, indicates improper concurrent modification
-                Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
-            }
-        }
-        synchronized (mMainRemote) {
-            mMainRemoteIsActive = false;
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if the given playback state is considered "active", i.e. it describes a state
-     * where playback is happening, or about to
-     * @param playState the playback state to evaluate
-     * @return true if active, false otherwise (inactive or unknown)
-     */
-    private static boolean isPlaystateActive(int playState) {
-        switch (playState) {
-            case RemoteControlClient.PLAYSTATE_PLAYING:
-            case RemoteControlClient.PLAYSTATE_BUFFERING:
-            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
-            case RemoteControlClient.PLAYSTATE_REWINDING:
-            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
-            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
-                return true;
-            default:
-                return false;
-        }
-    }
-
-    private void adjustRemoteVolume(int streamType, int direction, int flags) {
-        int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
-        boolean volFixed = false;
-        synchronized (mMainRemote) {
-            if (!mMainRemoteIsActive) {
-                if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
-                return;
-            }
-            rccId = mMainRemote.mRccId;
-            volFixed = (mMainRemote.mVolumeHandling ==
-                    RemoteControlClient.PLAYBACK_VOLUME_FIXED);
-        }
-        // unlike "local" stream volumes, we can't compute the new volume based on the direction,
-        // we can only notify the remote that volume needs to be updated, and we'll get an async'
-        // update through setPlaybackInfoForRcc()
-        if (!volFixed) {
-            sendVolumeUpdateToRemote(rccId, direction);
-        }
-
-        // fire up the UI
-        mVolumePanel.postRemoteVolumeChanged(streamType, flags);
-    }
-
-    private void sendVolumeUpdateToRemote(int rccId, int direction) {
-        if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
-        if (direction == 0) {
-            // only handling discrete events
-            return;
-        }
-        IRemoteVolumeObserver rvo = null;
-        synchronized (mRCStack) {
-            // The stack traversal order doesn't matter because there is only one stack entry
-            //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
-            //  start iterating from the top.
-            try {
-                for (int index = mRCStack.size()-1; index >= 0; index--) {
-                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
-                    //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
-                    if (rcse.mRccId == rccId) {
-                        rvo = rcse.mRemoteVolumeObs;
-                        break;
-                    }
-                }
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // not expected to happen, indicates improper concurrent modification
-                Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
-            }
-        }
-        if (rvo != null) {
-            try {
-                rvo.dispatchRemoteVolumeUpdate(direction, -1);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error dispatching relative volume update", e);
-            }
-        }
-    }
-
-    public int getRemoteStreamMaxVolume() {
-        synchronized (mMainRemote) {
-            if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
-                return 0;
-            }
-            return mMainRemote.mVolumeMax;
-        }
+        mMediaFocusControl.registerRemoteVolumeObserverForRcc(rccId, rvo);
     }
 
     public int getRemoteStreamVolume() {
-        synchronized (mMainRemote) {
-            if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
-                return 0;
-            }
-            return mMainRemote.mVolume;
-        }
+        return mMediaFocusControl.getRemoteStreamVolume();
     }
 
-    public void setRemoteStreamVolume(int vol) {
-        if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
-        int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
-        synchronized (mMainRemote) {
-            if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
-                return;
-            }
-            rccId = mMainRemote.mRccId;
-        }
-        IRemoteVolumeObserver rvo = null;
-        synchronized (mRCStack) {
-            // The stack traversal order doesn't matter because there is only one stack entry
-            //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
-            //  start iterating from the top.
-            try {
-                for (int index = mRCStack.size()-1; index >= 0; index--) {
-                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
-                    //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
-                    if (rcse.mRccId == rccId) {
-                        rvo = rcse.mRemoteVolumeObs;
-                        break;
-                    }
-                }
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // not expected to happen, indicates improper concurrent modification
-                Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
-            }
-        }
-        if (rvo != null) {
-            try {
-                rvo.dispatchRemoteVolumeUpdate(0, vol);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Error dispatching absolute volume update", e);
-            }
-        }
+    public int getRemoteStreamMaxVolume() {
+        return mMediaFocusControl.getRemoteStreamMaxVolume();
     }
 
-    /**
-     * Call to make AudioService reevaluate whether it's in a mode where remote players should
-     * have their volume controlled. In this implementation this is only to reset whether
-     * VolumePanel should display remote volumes
-     */
-    private void postReevaluateRemote() {
-        sendMsg(mAudioHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
+    public void setRemoteStreamVolume(int index) {
+        mMediaFocusControl.setRemoteStreamVolume(index);
     }
 
-    private void onReevaluateRemote() {
-        if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
-        // is there a registered RemoteControlClient that is handling remote playback
-        boolean hasRemotePlayback = false;
-        synchronized (mRCStack) {
-            // iteration stops when PLAYBACK_TYPE_REMOTE is found, so remote control stack
-            //   traversal order doesn't matter
-            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
-            while(stackIterator.hasNext()) {
-                RemoteControlStackEntry rcse = stackIterator.next();
-                if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
-                    hasRemotePlayback = true;
-                    break;
-                }
-            }
-        }
-        synchronized (mMainRemote) {
-            if (mHasRemotePlayback != hasRemotePlayback) {
-                mHasRemotePlayback = hasRemotePlayback;
-                mVolumePanel.postRemoteSliderVisibility(hasRemotePlayback);
-            }
-        }
+    public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
+        mMediaFocusControl.setPlaybackStateForRcc(rccId, state, timeMs, speed);
+    }
+
+    public void setPlaybackInfoForRcc(int rccId, int what, int value) {
+        mMediaFocusControl.setPlaybackInfoForRcc(rccId, what, value);
+    }
+
+    public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
+        mMediaFocusControl.dispatchMediaKeyEvent(keyEvent);
+    }
+
+    public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
+        mMediaFocusControl.dispatchMediaKeyEventUnderWakelock(keyEvent);
+    }
+
+    //==========================================================================================
+    // Audio Focus
+    //==========================================================================================
+    public int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb,
+            IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
+        return mMediaFocusControl.requestAudioFocus(mainStreamType, durationHint, cb, fd,
+                clientId, callingPackageName);
+    }
+
+    public int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId) {
+        return mMediaFocusControl.abandonAudioFocus(fd, clientId);
+    }
+
+    public void unregisterAudioFocusClient(String clientId) {
+        mMediaFocusControl.unregisterAudioFocusClient(clientId);
     }
 
     //==========================================================================================
@@ -6687,14 +4499,20 @@
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
 
-        dumpFocusStack(pw);
-        dumpRCStack(pw);
-        dumpRCCStack(pw);
-        dumpRCDList(pw);
+        mMediaFocusControl.dump(pw);
         dumpStreamStates(pw);
         dumpRingerMode(pw);
         pw.println("\nAudio routes:");
         pw.print("  mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
         pw.print("  mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
     }
+
+    // Inform AudioFlinger of our device's low RAM attribute
+    private static void readAndSetLowRamDevice()
+    {
+        int status = AudioSystem.setLowRamDevice(ActivityManager.isLowRamDeviceStatic());
+        if (status != 0) {
+            Log.w(TAG, "AudioFlinger informed of device's low RAM attribute; status " + status);
+        }
+    }
 }
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index d42bfd44..4805da5 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -403,4 +403,6 @@
     public static native int getPrimaryOutputFrameCount();
     public static native int getOutputLatency(int stream);
 
+    public static native int setLowRamDevice(boolean isLowRamDevice);
+
 }
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index e60ecc4..4a1646b 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -112,10 +112,10 @@
 
     oneway void setRemoteSubmixOn(boolean on, int address);
 
-    int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher l,
-            String clientId, String callingPackageName);
+    int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb,
+            IAudioFocusDispatcher fd, String clientId, String callingPackageName);
 
-    int abandonAudioFocus(IAudioFocusDispatcher l, String clientId);
+    int abandonAudioFocus(IAudioFocusDispatcher fd, String clientId);
     
     void unregisterAudioFocusClient(String clientId);
 
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
new file mode 100644
index 0000000..37c9e4a
--- /dev/null
+++ b/media/java/android/media/MediaFocusControl.java
@@ -0,0 +1,2446 @@
+/*
+ * 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.media;
+
+import android.app.Activity;
+import android.app.AppOpsManager;
+import android.app.KeyguardManager;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
+import android.app.PendingIntent.OnFinished;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.PowerManager;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.IBinder.DeathRecipient;
+import android.provider.Settings;
+import android.speech.RecognizerIntent;
+import android.telephony.PhoneStateListener;
+import android.telephony.TelephonyManager;
+import android.util.Log;
+import android.view.KeyEvent;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Stack;
+
+/**
+ * @hide
+ *
+ */
+public class MediaFocusControl implements OnFinished {
+
+    private static final String TAG = "MediaFocusControl";
+
+    /** Debug remote control client/display feature */
+    protected static final boolean DEBUG_RC = false;
+    /** Debug volumes */
+    protected static final boolean DEBUG_VOL = false;
+
+    /** Used to alter media button redirection when the phone is ringing. */
+    private boolean mIsRinging = false;
+
+    private final PowerManager.WakeLock mMediaEventWakeLock;
+    private final MediaEventHandler mEventHandler;
+    private final Context mContext;
+    private final ContentResolver mContentResolver;
+    private final VolumeController mVolumeController;
+    private final BroadcastReceiver mReceiver = new PackageIntentsReceiver();
+    private final AppOpsManager mAppOps;
+    private final KeyguardManager mKeyguardManager;
+    private final AudioService mAudioService;
+
+    protected MediaFocusControl(Looper looper, Context cntxt,
+            VolumeController volumeCtrl, AudioService as) {
+        mEventHandler = new MediaEventHandler(looper);
+        mContext = cntxt;
+        mContentResolver = mContext.getContentResolver();
+        mVolumeController = volumeCtrl;
+        mAudioService = as;
+
+        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
+        mMainRemote = new RemotePlaybackState(-1,
+                AudioService.getMaxStreamVolume(AudioManager.STREAM_MUSIC),
+                AudioService.getMaxStreamVolume(AudioManager.STREAM_MUSIC));
+
+        // Register for phone state monitoring
+        TelephonyManager tmgr = (TelephonyManager)
+                mContext.getSystemService(Context.TELEPHONY_SERVICE);
+        tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
+
+        // Register for package addition/removal/change intent broadcasts
+        //    for media button receiver persistence
+        IntentFilter pkgFilter = new IntentFilter();
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+        pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
+        pkgFilter.addDataScheme("package");
+        mContext.registerReceiver(mReceiver, pkgFilter);
+
+        mAppOps = (AppOpsManager)mContext.getSystemService(Context.APP_OPS_SERVICE);
+        mKeyguardManager =
+                (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+
+        mHasRemotePlayback = false;
+        mMainRemoteIsActive = false;
+        postReevaluateRemote();
+    }
+
+    // event handler messages
+    private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 0;
+    private static final int MSG_RCDISPLAY_CLEAR = 1;
+    private static final int MSG_RCDISPLAY_UPDATE = 2;
+    private static final int MSG_REEVALUATE_REMOTE = 3;
+    private static final int MSG_RCC_NEW_PLAYBACK_INFO = 4;
+    private static final int MSG_RCC_NEW_VOLUME_OBS = 5;
+    private static final int MSG_PROMOTE_RCC = 6;
+    private static final int MSG_RCC_NEW_PLAYBACK_STATE = 7;
+    private static final int MSG_RCC_SEEK_REQUEST = 8;
+
+    // sendMsg() flags
+    /** If the msg is already queued, replace it with this one. */
+    private static final int SENDMSG_REPLACE = 0;
+    /** If the msg is already queued, ignore this one and leave the old. */
+    private static final int SENDMSG_NOOP = 1;
+    /** If the msg is already queued, queue this one and leave the old. */
+    private static final int SENDMSG_QUEUE = 2;
+
+    private static void sendMsg(Handler handler, int msg,
+            int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
+
+        if (existingMsgPolicy == SENDMSG_REPLACE) {
+            handler.removeMessages(msg);
+        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
+            return;
+        }
+
+        handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
+    }
+
+    private class MediaEventHandler extends Handler {
+        MediaEventHandler(Looper looper) {
+            super(looper);
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case MSG_PERSIST_MEDIABUTTONRECEIVER:
+                    onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
+                    break;
+
+                case MSG_RCDISPLAY_CLEAR:
+                    onRcDisplayClear();
+                    break;
+
+                case MSG_RCDISPLAY_UPDATE:
+                    // msg.obj is guaranteed to be non null
+                    onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
+                    break;
+
+                case MSG_REEVALUATE_REMOTE:
+                    onReevaluateRemote();
+                    break;
+
+                case MSG_RCC_NEW_PLAYBACK_INFO:
+                    onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
+                            ((Integer)msg.obj).intValue() /* value */);
+                    break;
+                case MSG_RCC_NEW_VOLUME_OBS:
+                    onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
+                            (IRemoteVolumeObserver)msg.obj /* rvo */);
+                    break;
+                case MSG_RCC_NEW_PLAYBACK_STATE:
+                    onNewPlaybackStateForRcc(msg.arg1 /* rccId */,
+                            msg.arg2 /* state */,
+                            (RccPlaybackState)msg.obj /* newState */);
+                    break;
+                case MSG_RCC_SEEK_REQUEST:
+                    onSetRemoteControlClientPlaybackPosition(
+                            msg.arg1 /* generationId */, ((Long)msg.obj).longValue() /* timeMs */);
+
+                case MSG_PROMOTE_RCC:
+                    onPromoteRcc(msg.arg1);
+                    break;
+            }
+        }
+    }
+
+    protected void dump(PrintWriter pw) {
+        dumpFocusStack(pw);
+        dumpRCStack(pw);
+        dumpRCCStack(pw);
+        dumpRCDList(pw);
+    }
+
+    private class PackageIntentsReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
+                    || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
+                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
+                    // a package is being removed, not replaced
+                    String packageName = intent.getData().getSchemeSpecificPart();
+                    if (packageName != null) {
+                        cleanupMediaButtonReceiverForPackage(packageName, true);
+                    }
+                }
+            } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)
+                    || action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
+                String packageName = intent.getData().getSchemeSpecificPart();
+                if (packageName != null) {
+                    cleanupMediaButtonReceiverForPackage(packageName, false);
+                }
+            }
+        }
+    }
+
+    //==========================================================================================
+    // AudioFocus
+    //==========================================================================================
+
+    /* constant to identify focus stack entry that is used to hold the focus while the phone
+     * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
+     * entering and exiting calls.
+     */
+    protected final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
+
+    private final static Object mAudioFocusLock = new Object();
+
+    private final static Object mRingingLock = new Object();
+
+    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+        @Override
+        public void onCallStateChanged(int state, String incomingNumber) {
+            if (state == TelephonyManager.CALL_STATE_RINGING) {
+                //Log.v(TAG, " CALL_STATE_RINGING");
+                synchronized(mRingingLock) {
+                    mIsRinging = true;
+                }
+            } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
+                    || (state == TelephonyManager.CALL_STATE_IDLE)) {
+                synchronized(mRingingLock) {
+                    mIsRinging = false;
+                }
+            }
+        }
+    };
+
+    /**
+     * Discard the current audio focus owner.
+     * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
+     * focus), remove it from the stack, and clear the remote control display.
+     */
+    protected void discardAudioFocusOwner() {
+        synchronized(mAudioFocusLock) {
+            if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
+                // notify the current focus owner it lost focus after removing it from stack
+                FocusStackEntry focusOwner = mFocusStack.pop();
+                try {
+                    focusOwner.mFocusDispatcher.dispatchAudioFocusChange(
+                            AudioManager.AUDIOFOCUS_LOSS, focusOwner.mClientId);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failure to signal loss of audio focus due to "+ e);
+                    e.printStackTrace();
+                }
+                focusOwner.unlinkToDeath();
+                // clear RCD
+                synchronized(mRCStack) {
+                    clearRemoteControlDisplay_syncAfRcs();
+                }
+            }
+        }
+    }
+
+    private void notifyTopOfAudioFocusStack() {
+        // notify the top of the stack it gained focus
+        if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
+            if (canReassignAudioFocus()) {
+                try {
+                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
+                            AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private static class FocusStackEntry {
+        public int mStreamType = -1;// no stream type
+        public IAudioFocusDispatcher mFocusDispatcher = null;
+        public IBinder mSourceRef = null;
+        public String mClientId;
+        public int mFocusChangeType;
+        public AudioFocusDeathHandler mHandler;
+        public String mPackageName;
+        public int mCallingUid;
+
+        public FocusStackEntry() {
+        }
+
+        public FocusStackEntry(int streamType, int duration,
+                IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
+                String pn, int uid) {
+            mStreamType = streamType;
+            mFocusDispatcher = afl;
+            mSourceRef = source;
+            mClientId = id;
+            mFocusChangeType = duration;
+            mHandler = hdlr;
+            mPackageName = pn;
+            mCallingUid = uid;
+        }
+
+        public void unlinkToDeath() {
+            try {
+                if (mSourceRef != null && mHandler != null) {
+                    mSourceRef.unlinkToDeath(mHandler, 0);
+                    mHandler = null;
+                }
+            } catch (java.util.NoSuchElementException e) {
+                Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            unlinkToDeath(); // unlink exception handled inside method
+            super.finalize();
+        }
+    }
+
+    private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
+
+    /**
+     * Helper function:
+     * Display in the log the current entries in the audio focus stack
+     */
+    private void dumpFocusStack(PrintWriter pw) {
+        pw.println("\nAudio Focus stack entries (last is top of stack):");
+        synchronized(mAudioFocusLock) {
+            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
+            while(stackIterator.hasNext()) {
+                FocusStackEntry fse = stackIterator.next();
+                pw.println("  source:" + fse.mSourceRef
+                        + " -- pack: " + fse.mPackageName
+                        + " -- client: " + fse.mClientId
+                        + " -- duration: " + fse.mFocusChangeType
+                        + " -- uid: " + fse.mCallingUid
+                        + " -- stream: " + fse.mStreamType);
+            }
+        }
+    }
+
+    /**
+     * Helper function:
+     * Called synchronized on mAudioFocusLock
+     * Remove a focus listener from the focus stack.
+     * @param clientToRemove the focus listener
+     * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
+     *   focus, notify the next item in the stack it gained focus.
+     */
+    private void removeFocusStackEntry(String clientToRemove, boolean signal) {
+        // is the current top of the focus stack abandoning focus? (because of request, not death)
+        if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
+        {
+            //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
+            FocusStackEntry fse = mFocusStack.pop();
+            fse.unlinkToDeath();
+            if (signal) {
+                // notify the new top of the stack it gained focus
+                notifyTopOfAudioFocusStack();
+                // there's a new top of the stack, let the remote control know
+                synchronized(mRCStack) {
+                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+                }
+            }
+        } else {
+            // focus is abandoned by a client that's not at the top of the stack,
+            // no need to update focus.
+            // (using an iterator on the stack so we can safely remove an entry after having
+            //  evaluated it, traversal order doesn't matter here)
+            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
+            while(stackIterator.hasNext()) {
+                FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
+                if(fse.mClientId.equals(clientToRemove)) {
+                    Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
+                            + fse.mClientId);
+                    stackIterator.remove();
+                    fse.unlinkToDeath();
+                }
+            }
+        }
+    }
+
+    /**
+     * Helper function:
+     * Called synchronized on mAudioFocusLock
+     * Remove focus listeners from the focus stack for a particular client when it has died.
+     */
+    private void removeFocusStackEntryForClient(IBinder cb) {
+        // is the owner of the audio focus part of the client to remove?
+        boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
+                mFocusStack.peek().mSourceRef.equals(cb);
+        // (using an iterator on the stack so we can safely remove an entry after having
+        //  evaluated it, traversal order doesn't matter here)
+        Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
+        while(stackIterator.hasNext()) {
+            FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
+            if(fse.mSourceRef.equals(cb)) {
+                Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
+                        + fse.mClientId);
+                stackIterator.remove();
+                // the client just died, no need to unlink to its death
+            }
+        }
+        if (isTopOfStackForClientToRemove) {
+            // we removed an entry at the top of the stack:
+            //  notify the new top of the stack it gained focus.
+            notifyTopOfAudioFocusStack();
+            // there's a new top of the stack, let the remote control know
+            synchronized(mRCStack) {
+                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+            }
+        }
+    }
+
+    /**
+     * Helper function:
+     * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
+     */
+    private boolean canReassignAudioFocus() {
+        // focus requests are rejected during a phone call or when the phone is ringing
+        // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
+        if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Inner class to monitor audio focus client deaths, and remove them from the audio focus
+     * stack if necessary.
+     */
+    private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
+        private IBinder mCb; // To be notified of client's death
+
+        AudioFocusDeathHandler(IBinder cb) {
+            mCb = cb;
+        }
+
+        public void binderDied() {
+            synchronized(mAudioFocusLock) {
+                Log.w(TAG, "  AudioFocus   audio focus client died");
+                removeFocusStackEntryForClient(mCb);
+            }
+        }
+
+        public IBinder getBinder() {
+            return mCb;
+        }
+    }
+
+
+    /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int)  */
+    protected int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
+            IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
+        Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
+        // the main stream type for the audio focus request is currently not used. It may
+        // potentially be used to handle multiple stream type-dependent audio focuses.
+
+        // we need a valid binder callback for clients
+        if (!cb.pingBinder()) {
+            Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
+            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+        }
+
+        if (mAppOps.noteOp(AppOpsManager.OP_TAKE_AUDIO_FOCUS, Binder.getCallingUid(),
+                callingPackageName) != AppOpsManager.MODE_ALLOWED) {
+            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+        }
+
+        synchronized(mAudioFocusLock) {
+            if (!canReassignAudioFocus()) {
+                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+            }
+
+            // handle the potential premature death of the new holder of the focus
+            // (premature death == death before abandoning focus)
+            // Register for client death notification
+            AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
+            try {
+                cb.linkToDeath(afdh, 0);
+            } catch (RemoteException e) {
+                // client has already died!
+                Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
+                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
+            }
+
+            if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
+                // if focus is already owned by this client and the reason for acquiring the focus
+                // hasn't changed, don't do anything
+                if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
+                    // unlink death handler so it can be gc'ed.
+                    // linkToDeath() creates a JNI global reference preventing collection.
+                    cb.unlinkToDeath(afdh, 0);
+                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+                }
+                // the reason for the audio focus request has changed: remove the current top of
+                // stack and respond as if we had a new focus owner
+                FocusStackEntry fse = mFocusStack.pop();
+                fse.unlinkToDeath();
+            }
+
+            // notify current top of stack it is losing focus
+            if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
+                try {
+                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
+                            -1 * focusChangeHint, // loss and gain codes are inverse of each other
+                            mFocusStack.peek().mClientId);
+                } catch (RemoteException e) {
+                    Log.e(TAG, " Failure to signal loss of focus due to "+ e);
+                    e.printStackTrace();
+                }
+            }
+
+            // focus requester might already be somewhere below in the stack, remove it
+            removeFocusStackEntry(clientId, false /* signal */);
+
+            // push focus requester at the top of the audio focus stack
+            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
+                    clientId, afdh, callingPackageName, Binder.getCallingUid()));
+
+            // there's a new top of the stack, let the remote control know
+            synchronized(mRCStack) {
+                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+            }
+        }//synchronized(mAudioFocusLock)
+
+        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+    }
+
+    /** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener)  */
+    protected int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
+        Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
+        try {
+            // this will take care of notifying the new focus owner if needed
+            synchronized(mAudioFocusLock) {
+                removeFocusStackEntry(clientId, true);
+            }
+        } catch (java.util.ConcurrentModificationException cme) {
+            // Catching this exception here is temporary. It is here just to prevent
+            // a crash seen when the "Silent" notification is played. This is believed to be fixed
+            // but this try catch block is left just to be safe.
+            Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
+            cme.printStackTrace();
+        }
+
+        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+    }
+
+
+    protected void unregisterAudioFocusClient(String clientId) {
+        synchronized(mAudioFocusLock) {
+            removeFocusStackEntry(clientId, false);
+        }
+    }
+
+
+    //==========================================================================================
+    // RemoteControl
+    //==========================================================================================
+    protected void dispatchMediaKeyEvent(KeyEvent keyEvent) {
+        filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
+    }
+
+    protected void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
+        filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
+    }
+
+    private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
+        // sanity check on the incoming key event
+        if (!isValidMediaKeyEvent(keyEvent)) {
+            Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
+            return;
+        }
+        // event filtering for telephony
+        synchronized(mRingingLock) {
+            synchronized(mRCStack) {
+                if ((mMediaReceiverForCalls != null) &&
+                        (mIsRinging || (mAudioService.getMode() == AudioSystem.MODE_IN_CALL))) {
+                    dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
+                    return;
+                }
+            }
+        }
+        // event filtering based on voice-based interactions
+        if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
+            filterVoiceInputKeyEvent(keyEvent, needWakeLock);
+        } else {
+            dispatchMediaKeyEvent(keyEvent, needWakeLock);
+        }
+    }
+
+    /**
+     * Handles the dispatching of the media button events to the telephony package.
+     * Precondition: mMediaReceiverForCalls != null
+     * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
+     * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
+     *     is dispatched.
+     */
+    private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
+        Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+        keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+        keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
+        if (needWakeLock) {
+            mMediaEventWakeLock.acquire();
+            keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
+        }
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+                    null, mKeyEventDone, mEventHandler, Activity.RESULT_OK, null, null);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    /**
+     * Handles the dispatching of the media button events to one of the registered listeners,
+     * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
+     * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
+     * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
+     *     is dispatched.
+     */
+    private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
+        if (needWakeLock) {
+            mMediaEventWakeLock.acquire();
+        }
+        Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
+        keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
+        synchronized(mRCStack) {
+            if (!mRCStack.empty()) {
+                // send the intent that was registered by the client
+                try {
+                    mRCStack.peek().mMediaIntent.send(mContext,
+                            needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
+                            keyIntent, this, mEventHandler);
+                } catch (CanceledException e) {
+                    Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
+                    e.printStackTrace();
+                }
+            } else {
+                // legacy behavior when nobody registered their media button event receiver
+                //    through AudioManager
+                if (needWakeLock) {
+                    keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
+                }
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+                            null, mKeyEventDone,
+                            mEventHandler, Activity.RESULT_OK, null, null);
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        }
+    }
+
+    /**
+     * The different actions performed in response to a voice button key event.
+     */
+    private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
+    private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
+    private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
+
+    private final Object mVoiceEventLock = new Object();
+    private boolean mVoiceButtonDown;
+    private boolean mVoiceButtonHandled;
+
+    /**
+     * Filter key events that may be used for voice-based interactions
+     * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
+     *    media buttons that can be used to trigger voice-based interactions.
+     * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
+     *     is dispatched.
+     */
+    private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
+        if (DEBUG_RC) {
+            Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
+        }
+
+        int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
+        int keyAction = keyEvent.getAction();
+        synchronized (mVoiceEventLock) {
+            if (keyAction == KeyEvent.ACTION_DOWN) {
+                if (keyEvent.getRepeatCount() == 0) {
+                    // initial down
+                    mVoiceButtonDown = true;
+                    mVoiceButtonHandled = false;
+                } else if (mVoiceButtonDown && !mVoiceButtonHandled
+                        && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
+                    // long-press, start voice-based interactions
+                    mVoiceButtonHandled = true;
+                    voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
+                }
+            } else if (keyAction == KeyEvent.ACTION_UP) {
+                if (mVoiceButtonDown) {
+                    // voice button up
+                    mVoiceButtonDown = false;
+                    if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
+                        voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
+                    }
+                }
+            }
+        }//synchronized (mVoiceEventLock)
+
+        // take action after media button event filtering for voice-based interactions
+        switch (voiceButtonAction) {
+            case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
+                if (DEBUG_RC) Log.v(TAG, "   ignore key event");
+                break;
+            case VOICEBUTTON_ACTION_START_VOICE_INPUT:
+                if (DEBUG_RC) Log.v(TAG, "   start voice-based interactions");
+                // then start the voice-based interactions
+                startVoiceBasedInteractions(needWakeLock);
+                break;
+            case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
+                if (DEBUG_RC) Log.v(TAG, "   send simulated key event, wakelock=" + needWakeLock);
+                sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
+                break;
+        }
+    }
+
+    private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
+        // send DOWN event
+        KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
+        dispatchMediaKeyEvent(keyEvent, needWakeLock);
+        // send UP event
+        keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
+        dispatchMediaKeyEvent(keyEvent, needWakeLock);
+
+    }
+
+
+    private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
+        if (keyEvent == null) {
+            return false;
+        }
+        final int keyCode = keyEvent.getKeyCode();
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_MUTE:
+            case KeyEvent.KEYCODE_HEADSETHOOK:
+            case KeyEvent.KEYCODE_MEDIA_PLAY:
+            case KeyEvent.KEYCODE_MEDIA_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+            case KeyEvent.KEYCODE_MEDIA_STOP:
+            case KeyEvent.KEYCODE_MEDIA_NEXT:
+            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+            case KeyEvent.KEYCODE_MEDIA_REWIND:
+            case KeyEvent.KEYCODE_MEDIA_RECORD:
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+            case KeyEvent.KEYCODE_MEDIA_CLOSE:
+            case KeyEvent.KEYCODE_MEDIA_EJECT:
+            case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
+                break;
+            default:
+                return false;
+        }
+        return true;
+    }
+
+    /**
+     * Checks whether the given key code is one that can trigger the launch of voice-based
+     *   interactions.
+     * @param keyCode the key code associated with the key event
+     * @return true if the key is one of the supported voice-based interaction triggers
+     */
+    private static boolean isValidVoiceInputKeyCode(int keyCode) {
+        if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    /**
+     * Tell the system to start voice-based interactions / voice commands
+     */
+    private void startVoiceBasedInteractions(boolean needWakeLock) {
+        Intent voiceIntent = null;
+        // select which type of search to launch:
+        // - screen on and device unlocked: action is ACTION_WEB_SEARCH
+        // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
+        //    with EXTRA_SECURE set to true if the device is securely locked
+        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
+        if (!isLocked && pm.isScreenOn()) {
+            voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
+            Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
+        } else {
+            voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
+            voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
+                    isLocked && mKeyguardManager.isKeyguardSecure());
+            Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
+        }
+        // start the search activity
+        if (needWakeLock) {
+            mMediaEventWakeLock.acquire();
+        }
+        try {
+            if (voiceIntent != null) {
+                voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+                mContext.startActivity(voiceIntent);
+            }
+        } catch (ActivityNotFoundException e) {
+            Log.w(TAG, "No activity for search: " + e);
+        } finally {
+            if (needWakeLock) {
+                mMediaEventWakeLock.release();
+            }
+        }
+    }
+
+    private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
+
+    // only set when wakelock was acquired, no need to check value when received
+    private static final String EXTRA_WAKELOCK_ACQUIRED =
+            "android.media.AudioService.WAKELOCK_ACQUIRED";
+
+    public void onSendFinished(PendingIntent pendingIntent, Intent intent,
+            int resultCode, String resultData, Bundle resultExtras) {
+        if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
+            mMediaEventWakeLock.release();
+        }
+    }
+
+    BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
+        public void onReceive(Context context, Intent intent) {
+            if (intent == null) {
+                return;
+            }
+            Bundle extras = intent.getExtras();
+            if (extras == null) {
+                return;
+            }
+            if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
+                mMediaEventWakeLock.release();
+            }
+        }
+    };
+
+    /**
+     * Synchronization on mCurrentRcLock always inside a block synchronized on mRCStack
+     */
+    private final Object mCurrentRcLock = new Object();
+    /**
+     * The one remote control client which will receive a request for display information.
+     * This object may be null.
+     * Access protected by mCurrentRcLock.
+     */
+    private IRemoteControlClient mCurrentRcClient = null;
+
+    private final static int RC_INFO_NONE = 0;
+    private final static int RC_INFO_ALL =
+        RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
+        RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
+        RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
+        RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
+
+    /**
+     * A monotonically increasing generation counter for mCurrentRcClient.
+     * Only accessed with a lock on mCurrentRcLock.
+     * No value wrap-around issues as we only act on equal values.
+     */
+    private int mCurrentRcClientGen = 0;
+
+    /**
+     * Inner class to monitor remote control client deaths, and remove the client for the
+     * remote control stack if necessary.
+     */
+    private class RcClientDeathHandler implements IBinder.DeathRecipient {
+        final private IBinder mCb; // To be notified of client's death
+        final private PendingIntent mMediaIntent;
+
+        RcClientDeathHandler(IBinder cb, PendingIntent pi) {
+            mCb = cb;
+            mMediaIntent = pi;
+        }
+
+        public void binderDied() {
+            Log.w(TAG, "  RemoteControlClient died");
+            // remote control client died, make sure the displays don't use it anymore
+            //  by setting its remote control client to null
+            registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
+            // the dead client was maybe handling remote playback, reevaluate
+            postReevaluateRemote();
+        }
+
+        public IBinder getBinder() {
+            return mCb;
+        }
+    }
+
+    /**
+     * A global counter for RemoteControlClient identifiers
+     */
+    private static int sLastRccId = 0;
+
+    private class RemotePlaybackState {
+        int mRccId;
+        int mVolume;
+        int mVolumeMax;
+        int mVolumeHandling;
+
+        private RemotePlaybackState(int id, int vol, int volMax) {
+            mRccId = id;
+            mVolume = vol;
+            mVolumeMax = volMax;
+            mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
+        }
+    }
+
+    /**
+     * Internal cache for the playback information of the RemoteControlClient whose volume gets to
+     * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
+     * every time we need this info.
+     */
+    private RemotePlaybackState mMainRemote;
+    /**
+     * Indicates whether the "main" RemoteControlClient is considered active.
+     * Use synchronized on mMainRemote.
+     */
+    private boolean mMainRemoteIsActive;
+    /**
+     * Indicates whether there is remote playback going on. True even if there is no "active"
+     * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
+     * handles remote playback.
+     * Use synchronized on mMainRemote.
+     */
+    private boolean mHasRemotePlayback;
+
+    private static class RccPlaybackState {
+        public int mState;
+        public long mPositionMs;
+        public float mSpeed;
+
+        public RccPlaybackState(int state, long positionMs, float speed) {
+            mState = state;
+            mPositionMs = positionMs;
+            mSpeed = speed;
+        }
+
+        public void reset() {
+            mState = RemoteControlClient.PLAYSTATE_STOPPED;
+            mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID;
+            mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X;
+        }
+
+        @Override
+        public String toString() {
+            return stateToString() + ", "
+                    + ((mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) ?
+                            "PLAYBACK_POSITION_INVALID ," : String.valueOf(mPositionMs)) + "ms ,"
+                    + mSpeed + "X";
+        }
+
+        private String stateToString() {
+            switch (mState) {
+                case RemoteControlClient.PLAYSTATE_NONE:
+                    return "PLAYSTATE_NONE";
+                case RemoteControlClient.PLAYSTATE_STOPPED:
+                    return "PLAYSTATE_STOPPED";
+                case RemoteControlClient.PLAYSTATE_PAUSED:
+                    return "PLAYSTATE_PAUSED";
+                case RemoteControlClient.PLAYSTATE_PLAYING:
+                    return "PLAYSTATE_PLAYING";
+                case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+                    return "PLAYSTATE_FAST_FORWARDING";
+                case RemoteControlClient.PLAYSTATE_REWINDING:
+                    return "PLAYSTATE_REWINDING";
+                case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+                    return "PLAYSTATE_SKIPPING_FORWARDS";
+                case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+                    return "PLAYSTATE_SKIPPING_BACKWARDS";
+                case RemoteControlClient.PLAYSTATE_BUFFERING:
+                    return "PLAYSTATE_BUFFERING";
+                case RemoteControlClient.PLAYSTATE_ERROR:
+                    return "PLAYSTATE_ERROR";
+                default:
+                    return "[invalid playstate]";
+            }
+        }
+    }
+
+    protected static class RemoteControlStackEntry implements DeathRecipient {
+        public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+        final public MediaFocusControl mController;
+        /**
+         * The target for the ACTION_MEDIA_BUTTON events.
+         * Always non null.
+         */
+        final public PendingIntent mMediaIntent;
+        /**
+         * The registered media button event receiver.
+         * Always non null.
+         */
+        final public ComponentName mReceiverComponent;
+        public IBinder mToken;
+        public String mCallingPackageName;
+        public int mCallingUid;
+        /**
+         * Provides access to the information to display on the remote control.
+         * May be null (when a media button event receiver is registered,
+         *     but no remote control client has been registered) */
+        public IRemoteControlClient mRcClient;
+        public RcClientDeathHandler mRcClientDeathHandler;
+        /**
+         * Information only used for non-local playback
+         */
+        public int mPlaybackType;
+        public int mPlaybackVolume;
+        public int mPlaybackVolumeMax;
+        public int mPlaybackVolumeHandling;
+        public int mPlaybackStream;
+        public RccPlaybackState mPlaybackState;
+        public IRemoteVolumeObserver mRemoteVolumeObs;
+
+        public void resetPlaybackInfo() {
+            mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
+            mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
+            mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
+            mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
+            mPlaybackStream = AudioManager.STREAM_MUSIC;
+            mPlaybackState.reset();
+            mRemoteVolumeObs = null;
+        }
+
+        /** precondition: mediaIntent != null */
+        public RemoteControlStackEntry(MediaFocusControl controller, PendingIntent mediaIntent,
+                ComponentName eventReceiver, IBinder token) {
+            mController = controller;
+            mMediaIntent = mediaIntent;
+            mReceiverComponent = eventReceiver;
+            mToken = token;
+            mCallingUid = -1;
+            mRcClient = null;
+            mRccId = ++sLastRccId;
+            mPlaybackState = new RccPlaybackState(
+                    RemoteControlClient.PLAYSTATE_STOPPED,
+                    RemoteControlClient.PLAYBACK_POSITION_INVALID,
+                    RemoteControlClient.PLAYBACK_SPEED_1X);
+
+            resetPlaybackInfo();
+            if (mToken != null) {
+                try {
+                    mToken.linkToDeath(this, 0);
+                } catch (RemoteException e) {
+                    mController.mEventHandler.post(new Runnable() {
+                        @Override public void run() {
+                            mController.unregisterMediaButtonIntent(mMediaIntent);
+                        }
+                    });
+                }
+            }
+        }
+
+        public void unlinkToRcClientDeath() {
+            if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
+                try {
+                    mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
+                    mRcClientDeathHandler = null;
+                } catch (java.util.NoSuchElementException e) {
+                    // not much we can do here
+                    Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        public void destroy() {
+            unlinkToRcClientDeath();
+            if (mToken != null) {
+                mToken.unlinkToDeath(this, 0);
+                mToken = null;
+            }
+        }
+
+        @Override
+        public void binderDied() {
+            mController.unregisterMediaButtonIntent(mMediaIntent);
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            destroy(); // unlink exception handled inside method
+            super.finalize();
+        }
+    }
+
+    /**
+     *  The stack of remote control event receivers.
+     *  Code sections and methods that modify the remote control event receiver stack are
+     *  synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
+     *  stack, audio focus or RC, can lead to a change in the remote control display
+     */
+    private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
+
+    /**
+     * The component the telephony package can register so telephony calls have priority to
+     * handle media button events
+     */
+    private ComponentName mMediaReceiverForCalls = null;
+
+    /**
+     * Helper function:
+     * Display in the log the current entries in the remote control focus stack
+     */
+    private void dumpRCStack(PrintWriter pw) {
+        pw.println("\nRemote Control stack entries (last is top of stack):");
+        synchronized(mRCStack) {
+            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+            while(stackIterator.hasNext()) {
+                RemoteControlStackEntry rcse = stackIterator.next();
+                pw.println("  pi: " + rcse.mMediaIntent +
+                        " -- pack: " + rcse.mCallingPackageName +
+                        "  -- ercvr: " + rcse.mReceiverComponent +
+                        "  -- client: " + rcse.mRcClient +
+                        "  -- uid: " + rcse.mCallingUid +
+                        "  -- type: " + rcse.mPlaybackType +
+                        "  state: " + rcse.mPlaybackState);
+            }
+        }
+    }
+
+    /**
+     * Helper function:
+     * Display in the log the current entries in the remote control stack, focusing
+     * on RemoteControlClient data
+     */
+    private void dumpRCCStack(PrintWriter pw) {
+        pw.println("\nRemote Control Client stack entries (last is top of stack):");
+        synchronized(mRCStack) {
+            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+            while(stackIterator.hasNext()) {
+                RemoteControlStackEntry rcse = stackIterator.next();
+                pw.println("  uid: " + rcse.mCallingUid +
+                        "  -- id: " + rcse.mRccId +
+                        "  -- type: " + rcse.mPlaybackType +
+                        "  -- state: " + rcse.mPlaybackState +
+                        "  -- vol handling: " + rcse.mPlaybackVolumeHandling +
+                        "  -- vol: " + rcse.mPlaybackVolume +
+                        "  -- volMax: " + rcse.mPlaybackVolumeMax +
+                        "  -- volObs: " + rcse.mRemoteVolumeObs);
+            }
+            synchronized(mCurrentRcLock) {
+                pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen);
+            }
+        }
+        synchronized (mMainRemote) {
+            pw.println("\nRemote Volume State:");
+            pw.println("  has remote: " + mHasRemotePlayback);
+            pw.println("  is remote active: " + mMainRemoteIsActive);
+            pw.println("  rccId: " + mMainRemote.mRccId);
+            pw.println("  volume handling: "
+                    + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
+                            "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
+            pw.println("  volume: " + mMainRemote.mVolume);
+            pw.println("  volume steps: " + mMainRemote.mVolumeMax);
+        }
+    }
+
+    /**
+     * Helper function:
+     * Display in the log the current entries in the list of remote control displays
+     */
+    private void dumpRCDList(PrintWriter pw) {
+        pw.println("\nRemote Control Display list entries:");
+        synchronized(mRCStack) {
+            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext()) {
+                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+                pw.println("  IRCD: " + di.mRcDisplay +
+                        "  -- w:" + di.mArtworkExpectedWidth +
+                        "  -- h:" + di.mArtworkExpectedHeight+
+                        "  -- wantsPosSync:" + di.mWantsPositionSync);
+            }
+        }
+    }
+
+    /**
+     * Helper function:
+     * Remove any entry in the remote control stack that has the same package name as packageName
+     * Pre-condition: packageName != null
+     */
+    private void cleanupMediaButtonReceiverForPackage(String packageName, boolean removeAll) {
+        synchronized(mRCStack) {
+            if (mRCStack.empty()) {
+                return;
+            } else {
+                final PackageManager pm = mContext.getPackageManager();
+                RemoteControlStackEntry oldTop = mRCStack.peek();
+                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                // iterate over the stack entries
+                // (using an iterator on the stack so we can safely remove an entry after having
+                //  evaluated it, traversal order doesn't matter here)
+                while(stackIterator.hasNext()) {
+                    RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
+                    if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
+                        // a stack entry is from the package being removed, remove it from the stack
+                        stackIterator.remove();
+                        rcse.destroy();
+                    } else if (rcse.mReceiverComponent != null) {
+                        try {
+                            // Check to see if this receiver still exists.
+                            pm.getReceiverInfo(rcse.mReceiverComponent, 0);
+                        } catch (PackageManager.NameNotFoundException e) {
+                            // Not found -- remove it!
+                            stackIterator.remove();
+                            rcse.destroy();
+                        }
+                    }
+                }
+                if (mRCStack.empty()) {
+                    // no saved media button receiver
+                    mEventHandler.sendMessage(
+                            mEventHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
+                                    null));
+                } else if (oldTop != mRCStack.peek()) {
+                    // the top of the stack has changed, save it in the system settings
+                    // by posting a message to persist it; only do this however if it has
+                    // a concrete component name (is not a transient registration)
+                    RemoteControlStackEntry rcse = mRCStack.peek();
+                    if (rcse.mReceiverComponent != null) {
+                        mEventHandler.sendMessage(
+                                mEventHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
+                                        rcse.mReceiverComponent));
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Helper function:
+     * Restore remote control receiver from the system settings.
+     */
+    protected void restoreMediaButtonReceiver() {
+        String receiverName = Settings.System.getStringForUser(mContentResolver,
+                Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
+        if ((null != receiverName) && !receiverName.isEmpty()) {
+            ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
+            if (eventReceiver == null) {
+                // an invalid name was persisted
+                return;
+            }
+            // construct a PendingIntent targeted to the restored component name
+            // for the media button and register it
+            Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
+            //     the associated intent will be handled by the component being registered
+            mediaButtonIntent.setComponent(eventReceiver);
+            PendingIntent pi = PendingIntent.getBroadcast(mContext,
+                    0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
+            registerMediaButtonIntent(pi, eventReceiver, null);
+        }
+    }
+
+    /**
+     * Helper function:
+     * Set the new remote control receiver at the top of the RC focus stack.
+     * Called synchronized on mAudioFocusLock, then mRCStack
+     * precondition: mediaIntent != null
+     */
+    private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target,
+            IBinder token) {
+        // already at top of stack?
+        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
+            return;
+        }
+        if (mAppOps.noteOp(AppOpsManager.OP_TAKE_MEDIA_BUTTONS, Binder.getCallingUid(),
+                mediaIntent.getCreatorPackage()) != AppOpsManager.MODE_ALLOWED) {
+            return;
+        }
+        RemoteControlStackEntry rcse = null;
+        boolean wasInsideStack = false;
+        try {
+            for (int index = mRCStack.size()-1; index >= 0; index--) {
+                rcse = mRCStack.elementAt(index);
+                if(rcse.mMediaIntent.equals(mediaIntent)) {
+                    // ok to remove element while traversing the stack since we're leaving the loop
+                    mRCStack.removeElementAt(index);
+                    wasInsideStack = true;
+                    break;
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            // not expected to happen, indicates improper concurrent modification
+            Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
+        }
+        if (!wasInsideStack) {
+            rcse = new RemoteControlStackEntry(this, mediaIntent, target, token);
+        }
+        mRCStack.push(rcse); // rcse is never null
+
+        // post message to persist the default media button receiver
+        if (target != null) {
+            mEventHandler.sendMessage( mEventHandler.obtainMessage(
+                    MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
+        }
+    }
+
+    /**
+     * Helper function:
+     * Remove the remote control receiver from the RC focus stack.
+     * Called synchronized on mAudioFocusLock, then mRCStack
+     * precondition: pi != null
+     */
+    private void removeMediaButtonReceiver_syncAfRcs(PendingIntent pi) {
+        try {
+            for (int index = mRCStack.size()-1; index >= 0; index--) {
+                final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+                if (rcse.mMediaIntent.equals(pi)) {
+                    rcse.destroy();
+                    // ok to remove element while traversing the stack since we're leaving the loop
+                    mRCStack.removeElementAt(index);
+                    break;
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            // not expected to happen, indicates improper concurrent modification
+            Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
+        }
+    }
+
+    /**
+     * Helper function:
+     * Called synchronized on mRCStack
+     */
+    private boolean isCurrentRcController(PendingIntent pi) {
+        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
+            return true;
+        }
+        return false;
+    }
+
+    private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
+        Settings.System.putStringForUser(mContentResolver,
+                                         Settings.System.MEDIA_BUTTON_RECEIVER,
+                                         receiver == null ? "" : receiver.flattenToString(),
+                                         UserHandle.USER_CURRENT);
+    }
+
+    //==========================================================================================
+    // Remote control display / client
+    //==========================================================================================
+    /**
+     * Update the remote control displays with the new "focused" client generation
+     */
+    private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
+            PendingIntent newMediaIntent, boolean clearing) {
+        synchronized(mRCStack) {
+            if (mRcDisplays.size() > 0) {
+                final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+                while (displayIterator.hasNext()) {
+                    final DisplayInfoForServer di = displayIterator.next();
+                    try {
+                        di.mRcDisplay.setCurrentClientId(
+                                newClientGeneration, newMediaIntent, clearing);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc()",e);
+                        di.release();
+                        displayIterator.remove();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Update the remote control clients with the new "focused" client generation
+     */
+    private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
+        // (using an iterator on the stack so we can safely remove an entry if needed,
+        //  traversal order doesn't matter here as we update all entries)
+        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+        while(stackIterator.hasNext()) {
+            RemoteControlStackEntry se = stackIterator.next();
+            if ((se != null) && (se.mRcClient != null)) {
+                try {
+                    se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()",e);
+                    stackIterator.remove();
+                    se.unlinkToRcClientDeath();
+                }
+            }
+        }
+    }
+
+    /**
+     * Update the displays and clients with the new "focused" client generation and name
+     * @param newClientGeneration the new generation value matching a client update
+     * @param newMediaIntent the media button event receiver associated with the client.
+     *    May be null, which implies there is no registered media button event receiver.
+     * @param clearing true if the new client generation value maps to a remote control update
+     *    where the display should be cleared.
+     */
+    private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
+            PendingIntent newMediaIntent, boolean clearing) {
+        // send the new valid client generation ID to all displays
+        setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
+        // send the new valid client generation ID to all clients
+        setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
+    }
+
+    /**
+     * Called when processing MSG_RCDISPLAY_CLEAR event
+     */
+    private void onRcDisplayClear() {
+        if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
+
+        synchronized(mRCStack) {
+            synchronized(mCurrentRcLock) {
+                mCurrentRcClientGen++;
+                // synchronously update the displays and clients with the new client generation
+                setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
+                        null /*newMediaIntent*/, true /*clearing*/);
+            }
+        }
+    }
+
+    /**
+     * Called when processing MSG_RCDISPLAY_UPDATE event
+     */
+    private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
+        synchronized(mRCStack) {
+            synchronized(mCurrentRcLock) {
+                if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
+                    if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
+
+                    mCurrentRcClientGen++;
+                    // synchronously update the displays and clients with
+                    //      the new client generation
+                    setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
+                            rcse.mMediaIntent /*newMediaIntent*/,
+                            false /*clearing*/);
+
+                    // tell the current client that it needs to send info
+                    try {
+                        mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Current valid remote client is dead: "+e);
+                        mCurrentRcClient = null;
+                    }
+                } else {
+                    // the remote control display owner has changed between the
+                    // the message to update the display was sent, and the time it
+                    // gets to be processed (now)
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Helper function:
+     * Called synchronized on mRCStack
+     */
+    private void clearRemoteControlDisplay_syncAfRcs() {
+        synchronized(mCurrentRcLock) {
+            mCurrentRcClient = null;
+        }
+        // will cause onRcDisplayClear() to be called in AudioService's handler thread
+        mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
+    }
+
+    /**
+     * Helper function for code readability: only to be called from
+     *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
+     *    this method.
+     * Preconditions:
+     *    - called synchronized mAudioFocusLock then on mRCStack
+     *    - mRCStack.isEmpty() is false
+     */
+    private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
+        RemoteControlStackEntry rcse = mRCStack.peek();
+        int infoFlagsAboutToBeUsed = infoChangedFlags;
+        // this is where we enforce opt-in for information display on the remote controls
+        //   with the new AudioManager.registerRemoteControlClient() API
+        if (rcse.mRcClient == null) {
+            //Log.w(TAG, "Can't update remote control display with null remote control client");
+            clearRemoteControlDisplay_syncAfRcs();
+            return;
+        }
+        synchronized(mCurrentRcLock) {
+            if (!rcse.mRcClient.equals(mCurrentRcClient)) {
+                // new RC client, assume every type of information shall be queried
+                infoFlagsAboutToBeUsed = RC_INFO_ALL;
+            }
+            mCurrentRcClient = rcse.mRcClient;
+        }
+        // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
+        mEventHandler.sendMessage( mEventHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
+                infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
+    }
+
+    /**
+     * Helper function:
+     * Called synchronized on mAudioFocusLock, then mRCStack
+     * Check whether the remote control display should be updated, triggers the update if required
+     * @param infoChangedFlags the flags corresponding to the remote control client information
+     *     that has changed, if applicable (checking for the update conditions might trigger a
+     *     clear, rather than an update event).
+     */
+    private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
+        // determine whether the remote control display should be refreshed
+        // if either stack is empty, there is a mismatch, so clear the RC display
+        if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
+            clearRemoteControlDisplay_syncAfRcs();
+            return;
+        }
+
+        // determine which entry in the AudioFocus stack to consider, and compare against the
+        // top of the stack for the media button event receivers : simply using the top of the
+        // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
+        // notifications playing during music playback.
+        // Crawl the AudioFocus stack from the top until an entry is found with the following
+        // characteristics:
+        // - focus gain on STREAM_MUSIC stream
+        // - non-transient focus gain on a stream other than music
+        FocusStackEntry af = null;
+        try {
+            for (int index = mFocusStack.size()-1; index >= 0; index--) {
+                FocusStackEntry fse = mFocusStack.elementAt(index);
+                if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
+                        || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
+                    af = fse;
+                    break;
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e);
+            af = null;
+        }
+        if (af == null) {
+            clearRemoteControlDisplay_syncAfRcs();
+            return;
+        }
+
+        // if the audio focus and RC owners belong to different packages, there is a mismatch, clear
+        if ((mRCStack.peek().mCallingPackageName != null)
+                && (af.mPackageName != null)
+                && !(mRCStack.peek().mCallingPackageName.compareTo(
+                        af.mPackageName) == 0)) {
+            clearRemoteControlDisplay_syncAfRcs();
+            return;
+        }
+        // if the audio focus didn't originate from the same Uid as the one in which the remote
+        //   control information will be retrieved, clear
+        if (mRCStack.peek().mCallingUid != af.mCallingUid) {
+            clearRemoteControlDisplay_syncAfRcs();
+            return;
+        }
+
+        // refresh conditions were verified: update the remote controls
+        // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
+        updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
+    }
+
+    /**
+     * Helper function:
+     * Post a message to asynchronously move the media button event receiver associated with the
+     * given remote control client ID to the top of the remote control stack
+     * @param rccId
+     */
+    private void postPromoteRcc(int rccId) {
+        sendMsg(mEventHandler, MSG_PROMOTE_RCC, SENDMSG_REPLACE,
+                rccId /*arg1*/, 0, null, 0/*delay*/);
+    }
+
+    private void onPromoteRcc(int rccId) {
+        if (DEBUG_RC) { Log.d(TAG, "Promoting RCC " + rccId); }
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                // ignore if given RCC ID is already at top of remote control stack
+                if (!mRCStack.isEmpty() && (mRCStack.peek().mRccId == rccId)) {
+                    return;
+                }
+                int indexToPromote = -1;
+                try {
+                    for (int index = mRCStack.size()-1; index >= 0; index--) {
+                        final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+                        if (rcse.mRccId == rccId) {
+                            indexToPromote = index;
+                            break;
+                        }
+                    }
+                    if (indexToPromote >= 0) {
+                        if (DEBUG_RC) { Log.d(TAG, "  moving RCC from index " + indexToPromote
+                                + " to " + (mRCStack.size()-1)); }
+                        final RemoteControlStackEntry rcse = mRCStack.remove(indexToPromote);
+                        mRCStack.push(rcse);
+                        // the RC stack changed, reevaluate the display
+                        checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+                    }
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    // not expected to happen, indicates improper concurrent modification
+                    Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+                }
+            }//synchronized(mRCStack)
+        }//synchronized(mAudioFocusLock)
+    }
+
+    /**
+     * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
+     * precondition: mediaIntent != null
+     */
+    protected void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
+            IBinder token) {
+        Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
+
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token);
+                // new RC client, assume every type of information shall be queried
+                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+            }
+        }
+    }
+
+    /**
+     * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
+     * precondition: mediaIntent != null, eventReceiver != null
+     */
+    protected void unregisterMediaButtonIntent(PendingIntent mediaIntent)
+    {
+        Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
+
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
+                removeMediaButtonReceiver_syncAfRcs(mediaIntent);
+                if (topOfStackWillChange) {
+                    // current RC client will change, assume every type of info needs to be queried
+                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+                }
+            }
+        }
+    }
+
+    /**
+     * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
+     * precondition: c != null
+     */
+    protected void registerMediaButtonEventReceiverForCalls(ComponentName c) {
+        if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
+                != PackageManager.PERMISSION_GRANTED) {
+            Log.e(TAG, "Invalid permissions to register media button receiver for calls");
+            return;
+        }
+        synchronized(mRCStack) {
+            mMediaReceiverForCalls = c;
+        }
+    }
+
+    /**
+     * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
+     */
+    protected void unregisterMediaButtonEventReceiverForCalls() {
+        if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
+                != PackageManager.PERMISSION_GRANTED) {
+            Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
+            return;
+        }
+        synchronized(mRCStack) {
+            mMediaReceiverForCalls = null;
+        }
+    }
+
+    /**
+     * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
+     * @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient
+     * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
+     *     without modifying the RC stack, but while still causing the display to refresh (will
+     *     become blank as a result of this)
+     */
+    protected int registerRemoteControlClient(PendingIntent mediaIntent,
+            IRemoteControlClient rcClient, String callingPackageName) {
+        if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
+        int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                // store the new display information
+                try {
+                    for (int index = mRCStack.size()-1; index >= 0; index--) {
+                        final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+                        if(rcse.mMediaIntent.equals(mediaIntent)) {
+                            // already had a remote control client?
+                            if (rcse.mRcClientDeathHandler != null) {
+                                // stop monitoring the old client's death
+                                rcse.unlinkToRcClientDeath();
+                            }
+                            // save the new remote control client
+                            rcse.mRcClient = rcClient;
+                            rcse.mCallingPackageName = callingPackageName;
+                            rcse.mCallingUid = Binder.getCallingUid();
+                            if (rcClient == null) {
+                                // here rcse.mRcClientDeathHandler is null;
+                                rcse.resetPlaybackInfo();
+                                break;
+                            }
+                            rccId = rcse.mRccId;
+
+                            // there is a new (non-null) client:
+                            // 1/ give the new client the displays (if any)
+                            if (mRcDisplays.size() > 0) {
+                                plugRemoteControlDisplaysIntoClient_syncRcStack(rcse.mRcClient);
+                            }
+                            // 2/ monitor the new client's death
+                            IBinder b = rcse.mRcClient.asBinder();
+                            RcClientDeathHandler rcdh =
+                                    new RcClientDeathHandler(b, rcse.mMediaIntent);
+                            try {
+                                b.linkToDeath(rcdh, 0);
+                            } catch (RemoteException e) {
+                                // remote control client is DOA, disqualify it
+                                Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
+                                rcse.mRcClient = null;
+                            }
+                            rcse.mRcClientDeathHandler = rcdh;
+                            break;
+                        }
+                    }//for
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    // not expected to happen, indicates improper concurrent modification
+                    Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+                }
+
+                // if the eventReceiver is at the top of the stack
+                // then check for potential refresh of the remote controls
+                if (isCurrentRcController(mediaIntent)) {
+                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+                }
+            }//synchronized(mRCStack)
+        }//synchronized(mAudioFocusLock)
+        return rccId;
+    }
+
+    /**
+     * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
+     * rcClient is guaranteed non-null
+     */
+    protected void unregisterRemoteControlClient(PendingIntent mediaIntent,
+            IRemoteControlClient rcClient) {
+        if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient);
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                boolean topRccChange = false;
+                try {
+                    for (int index = mRCStack.size()-1; index >= 0; index--) {
+                        final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+                        if ((rcse.mMediaIntent.equals(mediaIntent))
+                                && rcClient.equals(rcse.mRcClient)) {
+                            // we found the IRemoteControlClient to unregister
+                            // stop monitoring its death
+                            rcse.unlinkToRcClientDeath();
+                            // reset the client-related fields
+                            rcse.mRcClient = null;
+                            rcse.mCallingPackageName = null;
+                            topRccChange = (index == mRCStack.size()-1);
+                            // there can only be one matching RCC in the RC stack, we're done
+                            break;
+                        }
+                    }
+                } catch (ArrayIndexOutOfBoundsException e) {
+                    // not expected to happen, indicates improper concurrent modification
+                    Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+                }
+                if (topRccChange) {
+                    // no more RCC for the RCD, check for potential refresh of the remote controls
+                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * A class to encapsulate all the information about a remote control display.
+     * After instanciation, init() must always be called before the object is added in the list
+     * of displays.
+     * Before being removed from the list of displays, release() must always be called (otherwise
+     * it will leak death handlers).
+     */
+    private class DisplayInfoForServer implements IBinder.DeathRecipient {
+        /** may never be null */
+        private IRemoteControlDisplay mRcDisplay;
+        private IBinder mRcDisplayBinder;
+        private int mArtworkExpectedWidth = -1;
+        private int mArtworkExpectedHeight = -1;
+        private boolean mWantsPositionSync = false;
+
+        public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {
+            if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h);
+            mRcDisplay = rcd;
+            mRcDisplayBinder = rcd.asBinder();
+            mArtworkExpectedWidth = w;
+            mArtworkExpectedHeight = h;
+        }
+
+        public boolean init() {
+            try {
+                mRcDisplayBinder.linkToDeath(this, 0);
+            } catch (RemoteException e) {
+                // remote control display is DOA, disqualify it
+                Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + mRcDisplayBinder);
+                return false;
+            }
+            return true;
+        }
+
+        public void release() {
+            try {
+                mRcDisplayBinder.unlinkToDeath(this, 0);
+            } catch (java.util.NoSuchElementException e) {
+                // not much we can do here, the display should have been unregistered anyway
+                Log.e(TAG, "Error in DisplaInfoForServer.relase()", e);
+            }
+        }
+
+        public void binderDied() {
+            synchronized(mRCStack) {
+                Log.w(TAG, "RemoteControl: display " + mRcDisplay + " died");
+                // remove the display from the list
+                final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+                while (displayIterator.hasNext()) {
+                    final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+                    if (di.mRcDisplay == mRcDisplay) {
+                        if (DEBUG_RC) Log.w(TAG, " RCD removed from list");
+                        displayIterator.remove();
+                        return;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * The remote control displays.
+     * Access synchronized on mRCStack
+     */
+    private ArrayList<DisplayInfoForServer> mRcDisplays = new ArrayList<DisplayInfoForServer>(1);
+
+    /**
+     * Plug each registered display into the specified client
+     * @param rcc, guaranteed non null
+     */
+    private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) {
+        final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+        while (displayIterator.hasNext()) {
+            final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+            try {
+                rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth,
+                        di.mArtworkExpectedHeight);
+                if (di.mWantsPositionSync) {
+                    rcc.setWantsSyncForDisplay(di.mRcDisplay, true);
+                }
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
+            }
+        }
+    }
+
+    /**
+     * Is the remote control display interface already registered
+     * @param rcd
+     * @return true if the IRemoteControlDisplay is already in the list of displays
+     */
+    private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) {
+        final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+        while (displayIterator.hasNext()) {
+            final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+            if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Register an IRemoteControlDisplay.
+     * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
+     * at the top of the stack to update the new display with its information.
+     * @see android.media.IAudioService#registerRemoteControlDisplay(android.media.IRemoteControlDisplay, int, int)
+     * @param rcd the IRemoteControlDisplay to register. No effect if null.
+     * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
+     * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
+     */
+    protected void registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
+        if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
+        synchronized(mAudioFocusLock) {
+            synchronized(mRCStack) {
+                if ((rcd == null) || rcDisplayIsPluggedIn_syncRcStack(rcd)) {
+                    return;
+                }
+                DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h);
+                if (!di.init()) {
+                    if (DEBUG_RC) Log.e(TAG, " error registering RCD");
+                    return;
+                }
+                // add RCD to list of displays
+                mRcDisplays.add(di);
+
+                // let all the remote control clients know there is a new display (so the remote
+                //   control stack traversal order doesn't matter).
+                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                while(stackIterator.hasNext()) {
+                    RemoteControlStackEntry rcse = stackIterator.next();
+                    if(rcse.mRcClient != null) {
+                        try {
+                            rcse.mRcClient.plugRemoteControlDisplay(rcd, w, h);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Error connecting RCD to client: ", e);
+                        }
+                    }
+                }
+
+                // we have a new display, of which all the clients are now aware: have it be updated
+                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
+            }
+        }
+    }
+
+    /**
+     * Unregister an IRemoteControlDisplay.
+     * No effect if the IRemoteControlDisplay hasn't been successfully registered.
+     * @see android.media.IAudioService#unregisterRemoteControlDisplay(android.media.IRemoteControlDisplay)
+     * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
+     */
+    protected void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
+        if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
+        synchronized(mRCStack) {
+            if (rcd == null) {
+                return;
+            }
+
+            boolean displayWasPluggedIn = false;
+            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext() && !displayWasPluggedIn) {
+                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+                    displayWasPluggedIn = true;
+                    di.release();
+                    displayIterator.remove();
+                }
+            }
+
+            if (displayWasPluggedIn) {
+                // disconnect this remote control display from all the clients, so the remote
+                //   control stack traversal order doesn't matter
+                final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                while(stackIterator.hasNext()) {
+                    final RemoteControlStackEntry rcse = stackIterator.next();
+                    if(rcse.mRcClient != null) {
+                        try {
+                            rcse.mRcClient.unplugRemoteControlDisplay(rcd);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Error disconnecting remote control display to client: ", e);
+                        }
+                    }
+                }
+            } else {
+                if (DEBUG_RC) Log.w(TAG, "  trying to unregister unregistered RCD");
+            }
+        }
+    }
+
+    /**
+     * Update the size of the artwork used by an IRemoteControlDisplay.
+     * @see android.media.IAudioService#remoteControlDisplayUsesBitmapSize(android.media.IRemoteControlDisplay, int, int)
+     * @param rcd the IRemoteControlDisplay with the new artwork size requirement
+     * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
+     * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
+     *   display doesn't need to receive artwork.
+     */
+    protected void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
+        synchronized(mRCStack) {
+            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+            boolean artworkSizeUpdate = false;
+            while (displayIterator.hasNext() && !artworkSizeUpdate) {
+                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+                    if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
+                        di.mArtworkExpectedWidth = w;
+                        di.mArtworkExpectedHeight = h;
+                        artworkSizeUpdate = true;
+                    }
+                }
+            }
+            if (artworkSizeUpdate) {
+                // RCD is currently plugged in and its artwork size has changed, notify all RCCs,
+                // stack traversal order doesn't matter
+                final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+                while(stackIterator.hasNext()) {
+                    final RemoteControlStackEntry rcse = stackIterator.next();
+                    if(rcse.mRcClient != null) {
+                        try {
+                            rcse.mRcClient.setBitmapSizeForDisplay(rcd, w, h);
+                        } catch (RemoteException e) {
+                            Log.e(TAG, "Error setting bitmap size for RCD on RCC: ", e);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Controls whether a remote control display needs periodic checks of the RemoteControlClient
+     * playback position to verify that the estimated position has not drifted from the actual
+     * position. By default the check is not performed.
+     * The IRemoteControlDisplay must have been previously registered for this to have any effect.
+     * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
+     *     or disabled. Not null.
+     * @param wantsSync if true, RemoteControlClient instances which expose their playback position
+     *     to the framework will regularly compare the estimated playback position with the actual
+     *     position, and will update the IRemoteControlDisplay implementation whenever a drift is
+     *     detected.
+     */
+    protected void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
+            boolean wantsSync) {
+        synchronized(mRCStack) {
+            boolean rcdRegistered = false;
+            // store the information about this display
+            // (display stack traversal order doesn't matter).
+            final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
+            while (displayIterator.hasNext()) {
+                final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
+                if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
+                    di.mWantsPositionSync = wantsSync;
+                    rcdRegistered = true;
+                    break;
+                }
+            }
+            if (!rcdRegistered) {
+                return;
+            }
+            // notify all current RemoteControlClients
+            // (stack traversal order doesn't matter as we notify all RCCs)
+            final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+            while (stackIterator.hasNext()) {
+                final RemoteControlStackEntry rcse = stackIterator.next();
+                if (rcse.mRcClient != null) {
+                    try {
+                        rcse.mRcClient.setWantsSyncForDisplay(rcd, wantsSync);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Error setting position sync flag for RCD on RCC: ", e);
+                    }
+                }
+            }
+        }
+    }
+
+    protected void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
+        // ignore position change requests if invalid generation ID
+        synchronized(mRCStack) {
+            synchronized(mCurrentRcLock) {
+                if (mCurrentRcClientGen != generationId) {
+                    return;
+                }
+            }
+        }
+        // discard any unprocessed seek request in the message queue, and replace with latest
+        sendMsg(mEventHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
+                0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
+    }
+
+    private void onSetRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
+        if(DEBUG_RC) Log.d(TAG, "onSetRemoteControlClientPlaybackPosition(genId=" + generationId +
+                ", timeMs=" + timeMs + ")");
+        synchronized(mRCStack) {
+            synchronized(mCurrentRcLock) {
+                if ((mCurrentRcClient != null) && (mCurrentRcClientGen == generationId)) {
+                    // tell the current client to seek to the requested location
+                    try {
+                        mCurrentRcClient.seekTo(generationId, timeMs);
+                    } catch (RemoteException e) {
+                        Log.e(TAG, "Current valid remote client is dead: "+e);
+                        mCurrentRcClient = null;
+                    }
+                }
+            }
+        }
+    }
+
+    protected void setPlaybackInfoForRcc(int rccId, int what, int value) {
+        sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE,
+                rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */);
+    }
+
+    // handler for MSG_RCC_NEW_PLAYBACK_INFO
+    private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
+        if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
+                ", what=" + key + ",val=" + value + ")");
+        synchronized(mRCStack) {
+            // iterating from top of stack as playback information changes are more likely
+            //   on entries at the top of the remote control stack
+            try {
+                for (int index = mRCStack.size()-1; index >= 0; index--) {
+                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+                    if (rcse.mRccId == rccId) {
+                        switch (key) {
+                            case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
+                                rcse.mPlaybackType = value;
+                                postReevaluateRemote();
+                                break;
+                            case RemoteControlClient.PLAYBACKINFO_VOLUME:
+                                rcse.mPlaybackVolume = value;
+                                synchronized (mMainRemote) {
+                                    if (rccId == mMainRemote.mRccId) {
+                                        mMainRemote.mVolume = value;
+                                        mVolumeController.postHasNewRemotePlaybackInfo();
+                                    }
+                                }
+                                break;
+                            case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
+                                rcse.mPlaybackVolumeMax = value;
+                                synchronized (mMainRemote) {
+                                    if (rccId == mMainRemote.mRccId) {
+                                        mMainRemote.mVolumeMax = value;
+                                        mVolumeController.postHasNewRemotePlaybackInfo();
+                                    }
+                                }
+                                break;
+                            case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
+                                rcse.mPlaybackVolumeHandling = value;
+                                synchronized (mMainRemote) {
+                                    if (rccId == mMainRemote.mRccId) {
+                                        mMainRemote.mVolumeHandling = value;
+                                        mVolumeController.postHasNewRemotePlaybackInfo();
+                                    }
+                                }
+                                break;
+                            case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
+                                rcse.mPlaybackStream = value;
+                                break;
+                            default:
+                                Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
+                                break;
+                        }
+                        return;
+                    }
+                }//for
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // not expected to happen, indicates improper concurrent modification
+                Log.e(TAG, "Wrong index mRCStack on onNewPlaybackInfoForRcc, lock error? ", e);
+            }
+        }
+    }
+
+    protected void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
+        sendMsg(mEventHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE,
+                rccId /* arg1 */, state /* arg2 */,
+                new RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
+    }
+
+    protected void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) {
+        if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
+                + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
+        synchronized(mRCStack) {
+            // iterating from top of stack as playback information changes are more likely
+            //   on entries at the top of the remote control stack
+            try {
+                for (int index = mRCStack.size()-1; index >= 0; index--) {
+                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+                    if (rcse.mRccId == rccId) {
+                        rcse.mPlaybackState = newState;
+                        synchronized (mMainRemote) {
+                            if (rccId == mMainRemote.mRccId) {
+                                mMainRemoteIsActive = isPlaystateActive(state);
+                                postReevaluateRemote();
+                            }
+                        }
+                        // an RCC moving to a "playing" state should become the media button
+                        //   event receiver so it can be controlled, without requiring the
+                        //   app to re-register its receiver
+                        if (isPlaystateActive(state)) {
+                            postPromoteRcc(rccId);
+                        }
+                    }
+                }//for
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // not expected to happen, indicates improper concurrent modification
+                Log.e(TAG, "Wrong index on mRCStack in onNewPlaybackStateForRcc, lock error? ", e);
+            }
+        }
+    }
+
+    protected void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
+        sendMsg(mEventHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
+                rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
+    }
+
+    // handler for MSG_RCC_NEW_VOLUME_OBS
+    private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
+        synchronized(mRCStack) {
+            // The stack traversal order doesn't matter because there is only one stack entry
+            //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
+            //  start iterating from the top.
+            try {
+                for (int index = mRCStack.size()-1; index >= 0; index--) {
+                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+                    if (rcse.mRccId == rccId) {
+                        rcse.mRemoteVolumeObs = rvo;
+                        break;
+                    }
+                }
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // not expected to happen, indicates improper concurrent modification
+                Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
+            }
+        }
+    }
+
+    /**
+     * Checks if a remote client is active on the supplied stream type. Update the remote stream
+     * volume state if found and playing
+     * @param streamType
+     * @return false if no remote playing is currently playing
+     */
+    protected boolean checkUpdateRemoteStateIfActive(int streamType) {
+        synchronized(mRCStack) {
+            // iterating from top of stack as active playback is more likely on entries at the top
+            try {
+                for (int index = mRCStack.size()-1; index >= 0; index--) {
+                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+                    if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
+                            && isPlaystateActive(rcse.mPlaybackState.mState)
+                            && (rcse.mPlaybackStream == streamType)) {
+                        if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
+                                + ", vol =" + rcse.mPlaybackVolume);
+                        synchronized (mMainRemote) {
+                            mMainRemote.mRccId = rcse.mRccId;
+                            mMainRemote.mVolume = rcse.mPlaybackVolume;
+                            mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax;
+                            mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling;
+                            mMainRemoteIsActive = true;
+                        }
+                        return true;
+                    }
+                }
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // not expected to happen, indicates improper concurrent modification
+                Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
+            }
+        }
+        synchronized (mMainRemote) {
+            mMainRemoteIsActive = false;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if the given playback state is considered "active", i.e. it describes a state
+     * where playback is happening, or about to
+     * @param playState the playback state to evaluate
+     * @return true if active, false otherwise (inactive or unknown)
+     */
+    private static boolean isPlaystateActive(int playState) {
+        switch (playState) {
+            case RemoteControlClient.PLAYSTATE_PLAYING:
+            case RemoteControlClient.PLAYSTATE_BUFFERING:
+            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
+            case RemoteControlClient.PLAYSTATE_REWINDING:
+            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
+            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    protected void adjustRemoteVolume(int streamType, int direction, int flags) {
+        int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+        boolean volFixed = false;
+        synchronized (mMainRemote) {
+            if (!mMainRemoteIsActive) {
+                if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
+                return;
+            }
+            rccId = mMainRemote.mRccId;
+            volFixed = (mMainRemote.mVolumeHandling ==
+                    RemoteControlClient.PLAYBACK_VOLUME_FIXED);
+        }
+        // unlike "local" stream volumes, we can't compute the new volume based on the direction,
+        // we can only notify the remote that volume needs to be updated, and we'll get an async'
+        // update through setPlaybackInfoForRcc()
+        if (!volFixed) {
+            sendVolumeUpdateToRemote(rccId, direction);
+        }
+
+        // fire up the UI
+        mVolumeController.postRemoteVolumeChanged(streamType, flags);
+    }
+
+    private void sendVolumeUpdateToRemote(int rccId, int direction) {
+        if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
+        if (direction == 0) {
+            // only handling discrete events
+            return;
+        }
+        IRemoteVolumeObserver rvo = null;
+        synchronized (mRCStack) {
+            // The stack traversal order doesn't matter because there is only one stack entry
+            //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
+            //  start iterating from the top.
+            try {
+                for (int index = mRCStack.size()-1; index >= 0; index--) {
+                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+                    //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
+                    if (rcse.mRccId == rccId) {
+                        rvo = rcse.mRemoteVolumeObs;
+                        break;
+                    }
+                }
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // not expected to happen, indicates improper concurrent modification
+                Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
+            }
+        }
+        if (rvo != null) {
+            try {
+                rvo.dispatchRemoteVolumeUpdate(direction, -1);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error dispatching relative volume update", e);
+            }
+        }
+    }
+
+    protected int getRemoteStreamMaxVolume() {
+        synchronized (mMainRemote) {
+            if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
+                return 0;
+            }
+            return mMainRemote.mVolumeMax;
+        }
+    }
+
+    protected int getRemoteStreamVolume() {
+        synchronized (mMainRemote) {
+            if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
+                return 0;
+            }
+            return mMainRemote.mVolume;
+        }
+    }
+
+    protected void setRemoteStreamVolume(int vol) {
+        if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
+        int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
+        synchronized (mMainRemote) {
+            if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
+                return;
+            }
+            rccId = mMainRemote.mRccId;
+        }
+        IRemoteVolumeObserver rvo = null;
+        synchronized (mRCStack) {
+            // The stack traversal order doesn't matter because there is only one stack entry
+            //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
+            //  start iterating from the top.
+            try {
+                for (int index = mRCStack.size()-1; index >= 0; index--) {
+                    final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
+                    //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
+                    if (rcse.mRccId == rccId) {
+                        rvo = rcse.mRemoteVolumeObs;
+                        break;
+                    }
+                }
+            } catch (ArrayIndexOutOfBoundsException e) {
+                // not expected to happen, indicates improper concurrent modification
+                Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
+            }
+        }
+        if (rvo != null) {
+            try {
+                rvo.dispatchRemoteVolumeUpdate(0, vol);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Error dispatching absolute volume update", e);
+            }
+        }
+    }
+
+    /**
+     * Call to make AudioService reevaluate whether it's in a mode where remote players should
+     * have their volume controlled. In this implementation this is only to reset whether
+     * VolumePanel should display remote volumes
+     */
+    private void postReevaluateRemote() {
+        sendMsg(mEventHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
+    }
+
+    private void onReevaluateRemote() {
+        if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
+        // is there a registered RemoteControlClient that is handling remote playback
+        boolean hasRemotePlayback = false;
+        synchronized (mRCStack) {
+            // iteration stops when PLAYBACK_TYPE_REMOTE is found, so remote control stack
+            //   traversal order doesn't matter
+            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
+            while(stackIterator.hasNext()) {
+                RemoteControlStackEntry rcse = stackIterator.next();
+                if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
+                    hasRemotePlayback = true;
+                    break;
+                }
+            }
+        }
+        synchronized (mMainRemote) {
+            if (mHasRemotePlayback != hasRemotePlayback) {
+                mHasRemotePlayback = hasRemotePlayback;
+                mVolumeController.postRemoteSliderVisibility(hasRemotePlayback);
+            }
+        }
+    }
+
+}
diff --git a/media/java/android/media/VolumeController.java b/media/java/android/media/VolumeController.java
new file mode 100644
index 0000000..2d12bf2
--- /dev/null
+++ b/media/java/android/media/VolumeController.java
@@ -0,0 +1,29 @@
+/*
+ * 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.media;
+
+/**
+ * @hide
+ */
+public interface VolumeController {
+
+    public void postHasNewRemotePlaybackInfo();
+
+    public void postRemoteVolumeChanged(int streamType, int flags);
+
+    public void postRemoteSliderVisibility(boolean visible);
+}
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 7866df4..d876bd2 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, true, 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/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
index f0fc817..2ebe11a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
@@ -20,7 +20,7 @@
     protected static final int USE_CALLING_UID = -1;
     protected static final int BAD_VALUE = -22;
     protected static final int ALREADY_EXISTS = -17;
-    protected static final int NO_ERROR = 0;
+    public static final int NO_ERROR = 0;
     private final Context mContext;
 
     public CameraBinderTestUtils(Context context) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index 0cbb989..1f478d2 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -18,6 +18,7 @@
 
 import android.graphics.SurfaceTexture;
 import android.hardware.photography.CameraMetadata;
+import android.hardware.photography.CameraPropertiesKeys;
 import android.hardware.photography.CaptureRequest;
 import android.hardware.photography.ICameraDeviceCallbacks;
 import android.hardware.photography.ICameraDeviceUser;
@@ -294,4 +295,16 @@
         request.close();
         metadata.close();
     }
+
+    @SmallTest
+    public void testCameraInfo() throws RemoteException {
+        CameraMetadata info = new CameraMetadata();
+
+        int status = mCameraUser.getCameraInfo(/*out*/info);
+        assertEquals(CameraBinderTestUtils.NO_ERROR, status);
+
+        assertFalse(info.isEmpty());
+        assertNotNull(info.get(CameraPropertiesKeys.Scaler.AVAILABLE_FORMATS));
+    }
+
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 3400434..131441b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -18,7 +18,20 @@
 
 import android.os.Parcel;
 import android.test.suitebuilder.annotation.SmallTest;
+import android.graphics.ImageFormat;
+import android.graphics.Rect;
 import android.hardware.photography.CameraMetadata;
+import android.hardware.photography.Rational;
+import android.hardware.photography.Size;
+
+import static android.hardware.photography.CameraMetadata.*;
+
+import java.lang.reflect.Array;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+
+import static org.junit.Assert.assertArrayEquals;
 
 /**
  * <pre>
@@ -32,6 +45,21 @@
     CameraMetadata mMetadata;
     Parcel mParcel;
 
+    // Sections
+    static final int ANDROID_COLOR_CORRECTION = 0;
+    static final int ANDROID_CONTROL = 1;
+
+    // Section starts
+    static final int ANDROID_COLOR_CORRECTION_START = ANDROID_COLOR_CORRECTION << 16;
+    static final int ANDROID_CONTROL_START = ANDROID_CONTROL << 16;
+
+    // Tags
+    static final int ANDROID_COLOR_CORRECTION_MODE = ANDROID_COLOR_CORRECTION_START;
+    static final int ANDROID_COLOR_CORRECTION_TRANSFORM = ANDROID_COLOR_CORRECTION_START + 1;
+
+    static final int ANDROID_CONTROL_AE_ANTIBANDING_MODE = ANDROID_CONTROL_START;
+    static final int ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION = ANDROID_CONTROL_START + 1;
+
     @Override
     public void setUp() {
         mMetadata = new CameraMetadata();
@@ -105,5 +133,462 @@
         } catch (IllegalStateException e) {
          // good: we expect calling this method after close to fail
         }
+
+        try {
+            mMetadata.readValues(/*tag*/0);
+            fail("Unreachable -- readValues after close should throw IllegalStateException");
+        } catch (IllegalStateException e) {
+         // good: we expect calling this method after close to fail
+        }
+
+        try {
+            mMetadata.writeValues(/*tag*/0, /*source*/new byte[] { 1,2,3 });
+            fail("Unreachable -- readValues after close should throw IllegalStateException");
+        } catch (IllegalStateException e) {
+         // good: we expect calling this method after close to fail
+        }
+    }
+
+    @SmallTest
+    public void testGetTagFromKey() {
+
+        // Test success
+
+        assertEquals(ANDROID_COLOR_CORRECTION_MODE,
+                CameraMetadata.getTag("android.colorCorrection.mode"));
+        assertEquals(ANDROID_COLOR_CORRECTION_TRANSFORM,
+                CameraMetadata.getTag("android.colorCorrection.transform"));
+        assertEquals(ANDROID_CONTROL_AE_ANTIBANDING_MODE,
+                CameraMetadata.getTag("android.control.aeAntibandingMode"));
+        assertEquals(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION,
+                CameraMetadata.getTag("android.control.aeExposureCompensation"));
+
+        // Test failures
+
+        try {
+            CameraMetadata.getTag(null);
+            fail("A null key should throw NPE");
+        } catch(NullPointerException e) {
+        }
+
+        try {
+            CameraMetadata.getTag("android.control");
+            fail("A section name only should not be a valid key");
+        } catch(IllegalArgumentException e) {
+        }
+
+        try {
+            CameraMetadata.getTag("android.control.thisTagNameIsFakeAndDoesNotExist");
+            fail("A valid section with an invalid tag name should not be a valid key");
+        } catch(IllegalArgumentException e) {
+        }
+
+        try {
+            CameraMetadata.getTag("android");
+            fail("A namespace name only should not be a valid key");
+        } catch(IllegalArgumentException e) {
+        }
+
+        try {
+            CameraMetadata.getTag("this.key.is.definitely.invalid");
+            fail("A completely fake key name should not be valid");
+        } catch(IllegalArgumentException e) {
+        }
+    }
+
+    @SmallTest
+    public void testGetTypeFromTag() {
+        assertEquals(TYPE_BYTE, CameraMetadata.getNativeType(ANDROID_COLOR_CORRECTION_MODE));
+        assertEquals(TYPE_FLOAT, CameraMetadata.getNativeType(ANDROID_COLOR_CORRECTION_TRANSFORM));
+        assertEquals(TYPE_BYTE, CameraMetadata.getNativeType(ANDROID_CONTROL_AE_ANTIBANDING_MODE));
+        assertEquals(TYPE_INT32,
+                CameraMetadata.getNativeType(ANDROID_CONTROL_AE_EXPOSURE_COMPENSATION));
+
+        try {
+            CameraMetadata.getNativeType(0xDEADF00D);
+            fail("No type should exist for invalid tag 0xDEADF00D");
+        } catch(IllegalArgumentException e) {
+        }
+    }
+
+    @SmallTest
+    public void testReadWriteValues() {
+        final byte ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY = 2;
+        byte[] valueResult;
+
+        assertEquals(0, mMetadata.getEntryCount());
+        assertEquals(true, mMetadata.isEmpty());
+
+        //
+        // android.colorCorrection.mode (single enum byte)
+        //
+
+        assertEquals(null, mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE));
+
+        // Write/read null values
+        mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, null);
+        assertEquals(null, mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE));
+
+        // Write 0 values
+        mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, new byte[] {});
+
+        // Read 0 values
+        valueResult = mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE);
+        assertNotNull(valueResult);
+        assertEquals(0, valueResult.length);
+
+        assertEquals(1, mMetadata.getEntryCount());
+        assertEquals(false, mMetadata.isEmpty());
+
+        // Write 1 value
+        mMetadata.writeValues(ANDROID_COLOR_CORRECTION_MODE, new byte[] {
+            ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY
+        });
+
+        // Read 1 value
+        valueResult = mMetadata.readValues(ANDROID_COLOR_CORRECTION_MODE);
+        assertNotNull(valueResult);
+        assertEquals(1, valueResult.length);
+        assertEquals(ANDROID_COLOR_CORRECTION_MODE_HIGH_QUALITY, valueResult[0]);
+
+        assertEquals(1, mMetadata.getEntryCount());
+        assertEquals(false, mMetadata.isEmpty());
+
+        //
+        // android.colorCorrection.transform (3x3 matrix)
+        //
+
+        final float[] transformMatrix = new float[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
+        byte[] transformMatrixAsByteArray = new byte[transformMatrix.length * 4];
+        ByteBuffer transformMatrixByteBuffer =
+                ByteBuffer.wrap(transformMatrixAsByteArray).order(ByteOrder.nativeOrder());
+        for (float f : transformMatrix)
+            transformMatrixByteBuffer.putFloat(f);
+
+        // Read
+        assertNull(mMetadata.readValues(ANDROID_COLOR_CORRECTION_TRANSFORM));
+        mMetadata.writeValues(ANDROID_COLOR_CORRECTION_TRANSFORM, transformMatrixAsByteArray);
+
+        // Write
+        assertArrayEquals(transformMatrixAsByteArray,
+                mMetadata.readValues(ANDROID_COLOR_CORRECTION_TRANSFORM));
+
+        assertEquals(2, mMetadata.getEntryCount());
+        assertEquals(false, mMetadata.isEmpty());
+
+        // Erase
+        mMetadata.writeValues(ANDROID_COLOR_CORRECTION_TRANSFORM, null);
+        assertNull(mMetadata.readValues(ANDROID_COLOR_CORRECTION_TRANSFORM));
+        assertEquals(1, mMetadata.getEntryCount());
+    }
+
+    private static <T> void assertArrayEquals(T expected, T actual) {
+        assertEquals(Array.getLength(expected), Array.getLength(actual));
+
+        int len = Array.getLength(expected);
+        for (int i = 0; i < len; ++i) {
+            assertEquals(Array.get(expected, i), Array.get(actual, i));
+        }
+    }
+
+    private <T> void checkKeyGetAndSet(String keyStr, Class<T> type, T value) {
+        assertFalse("Use checkKeyGetAndSetArray to compare array Keys", type.isArray());
+
+        Key<T> key = new Key<T>(keyStr, type);
+        assertNull(mMetadata.get(key));
+        mMetadata.set(key, null);
+        assertNull(mMetadata.get(key));
+        mMetadata.set(key, value);
+
+        T actual = mMetadata.get(key);
+        assertEquals(value, actual);
+    }
+
+    private <T> void checkKeyGetAndSetArray(String keyStr, Class<T> type, T value) {
+        assertTrue(type.isArray());
+
+        Key<T> key = new Key<T>(keyStr, type);
+        assertNull(mMetadata.get(key));
+        mMetadata.set(key, value);
+        assertArrayEquals(value, mMetadata.get(key));
+    }
+
+    @SmallTest
+    public void testReadWritePrimitive() {
+        // int32 (single)
+        checkKeyGetAndSet("android.control.aeExposureCompensation", Integer.TYPE, 0xC0FFEE);
+
+        // byte (single)
+        checkKeyGetAndSet("android.flash.maxEnergy", Byte.TYPE, (byte)6);
+
+        // int64 (single)
+        checkKeyGetAndSet("android.flash.firingTime", Long.TYPE, 0xABCD12345678FFFFL);
+
+        // float (single)
+        checkKeyGetAndSet("android.lens.aperture", Float.TYPE, Float.MAX_VALUE);
+
+        // double (single) -- technically double x 3, but we fake it
+        checkKeyGetAndSet("android.jpeg.gpsCoordinates", Double.TYPE, Double.MAX_VALUE);
+
+        // rational (single)
+        checkKeyGetAndSet("android.sensor.baseGainFactor", Rational.class, new Rational(1, 2));
+
+        /**
+         * Weirder cases, that don't map 1:1 with the native types
+         */
+
+        // bool (single) -- with TYPE_BYTE
+        checkKeyGetAndSet("android.control.aeLock", Boolean.TYPE, true);
+
+        // integer (single) -- with TYPE_BYTE
+        checkKeyGetAndSet("android.control.aePrecaptureTrigger", Integer.TYPE, 6);
+    }
+
+    @SmallTest
+    public void testReadWritePrimitiveArray() {
+        // int32 (n)
+        checkKeyGetAndSetArray("android.sensor.info.availableSensitivities", int[].class,
+                new int[] {
+                        0xC0FFEE, 0xDEADF00D
+                });
+
+        // byte (n)
+        checkKeyGetAndSetArray("android.statistics.faceScores", byte[].class, new byte[] {
+                1, 2, 3, 4
+        });
+
+        // int64 (n)
+        checkKeyGetAndSetArray("android.scaler.availableProcessedMinDurations", long[].class,
+                new long[] {
+                        0xABCD12345678FFFFL, 0x1234ABCD5678FFFFL, 0xFFFF12345678ABCDL
+                });
+
+        // float (n)
+        checkKeyGetAndSetArray("android.lens.info.availableApertures", float[].class,
+                new float[] {
+                        Float.MAX_VALUE, Float.MIN_NORMAL, Float.MIN_VALUE
+                });
+
+        // double (n) -- in particular double x 3
+        checkKeyGetAndSetArray("android.jpeg.gpsCoordinates", double[].class,
+                new double[] {
+                        Double.MAX_VALUE, Double.MIN_NORMAL, Double.MIN_VALUE
+                });
+
+        // rational (n) -- in particular rational x 9
+        checkKeyGetAndSetArray("android.sensor.calibrationTransform1", Rational[].class,
+                new Rational[] {
+                        new Rational(1, 2), new Rational(3, 4), new Rational(5, 6),
+                        new Rational(7, 8), new Rational(9, 10), new Rational(10, 11),
+                        new Rational(12, 13), new Rational(14, 15), new Rational(15, 16)
+                });
+
+        /**
+         * Weirder cases, that don't map 1:1 with the native types
+         */
+
+        // bool (n) -- with TYPE_BYTE
+        checkKeyGetAndSetArray("android.control.aeLock", boolean[].class, new boolean[] {
+                true, false, true
+        });
+
+
+        // integer (n) -- with TYPE_BYTE
+        checkKeyGetAndSetArray("android.control.aeAvailableModes", int[].class, new int[] {
+            1, 2, 3, 4
+        });
+    }
+
+    private enum ColorCorrectionMode {
+        TRANSFORM_MATRIX,
+        FAST,
+        HIGH_QUALITY
+    }
+
+    private enum AeAntibandingMode {
+        OFF,
+        _50HZ,
+        _60HZ,
+        AUTO
+    }
+
+    // TODO: special values for the enum.
+    private enum AvailableFormat {
+        RAW_SENSOR,
+        YV12,
+        YCrCb_420_SP,
+        IMPLEMENTATION_DEFINED,
+        YCbCr_420_888,
+        BLOB
+    }
+
+    @SmallTest
+    public void testReadWriteEnum() {
+        // byte (single)
+        checkKeyGetAndSet("android.colorCorrection.mode", ColorCorrectionMode.class,
+                ColorCorrectionMode.HIGH_QUALITY);
+
+        // byte (single)
+        checkKeyGetAndSet("android.control.aeAntibandingMode", AeAntibandingMode.class,
+                AeAntibandingMode.AUTO);
+
+        // byte (n)
+        checkKeyGetAndSetArray("android.control.aeAvailableAntibandingModes",
+                AeAntibandingMode[].class, new AeAntibandingMode[] {
+                        AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ,
+                        AeAntibandingMode.AUTO
+                });
+
+        /**
+         * Stranger cases that don't use byte enums
+         */
+        // int (n)
+        checkKeyGetAndSetArray("android.scaler.availableFormats", AvailableFormat[].class,
+                new AvailableFormat[] {
+                        AvailableFormat.RAW_SENSOR,
+                        AvailableFormat.YV12,
+                        AvailableFormat.IMPLEMENTATION_DEFINED
+                });
+
+    }
+
+    @SmallTest
+    public void testReadWriteEnumWithCustomValues() {
+        CameraMetadata.registerEnumValues(AeAntibandingMode.class, new int[] {
+            0,
+            10,
+            20,
+            30
+        });
+
+        // byte (single)
+        checkKeyGetAndSet("android.control.aeAntibandingMode", AeAntibandingMode.class,
+                AeAntibandingMode.AUTO);
+
+        // byte (n)
+        checkKeyGetAndSetArray("android.control.aeAvailableAntibandingModes",
+                AeAntibandingMode[].class, new AeAntibandingMode[] {
+                        AeAntibandingMode.OFF, AeAntibandingMode._50HZ, AeAntibandingMode._60HZ,
+                        AeAntibandingMode.AUTO
+                });
+
+        Key<AeAntibandingMode[]> aeAntibandingModeKey =
+                new Key<AeAntibandingMode[]>("android.control.aeAvailableAntibandingModes",
+                        AeAntibandingMode[].class);
+        byte[] aeAntibandingModeValues = mMetadata.readValues(CameraMetadata
+                .getTag("android.control.aeAvailableAntibandingModes"));
+        byte[] expectedValues = new byte[] { 0, 10, 20, 30 };
+        assertArrayEquals(expectedValues, aeAntibandingModeValues);
+
+
+        /**
+         * Stranger cases that don't use byte enums
+         */
+        // int (n)
+        CameraMetadata.registerEnumValues(AvailableFormat.class, new int[] {
+            0x20,
+            0x32315659,
+            0x11,
+            0x22,
+            0x23,
+            0x21,
+        });
+
+        checkKeyGetAndSetArray("android.scaler.availableFormats", AvailableFormat[].class,
+                new AvailableFormat[] {
+                        AvailableFormat.RAW_SENSOR,
+                        AvailableFormat.YV12,
+                        AvailableFormat.IMPLEMENTATION_DEFINED,
+                        AvailableFormat.YCbCr_420_888
+                });
+
+        Key<AeAntibandingMode> availableFormatsKey =
+                new Key<AeAntibandingMode>("android.scaler.availableFormats",
+                        AeAntibandingMode.class);
+        byte[] availableFormatValues = mMetadata.readValues(CameraMetadata
+                .getTag(availableFormatsKey.getName()));
+
+        int[] expectedIntValues = new int[] {
+                0x20,
+                0x32315659,
+                0x22,
+                0x23
+        };
+
+        ByteBuffer bf = ByteBuffer.wrap(availableFormatValues).order(ByteOrder.nativeOrder());
+
+        assertEquals(expectedIntValues.length * 4, availableFormatValues.length);
+        for (int i = 0; i < expectedIntValues.length; ++i) {
+            assertEquals(expectedIntValues[i], bf.getInt());
+        }
+    }
+
+    @SmallTest
+    public void testReadWriteSize() {
+        // int32 x n
+        checkKeyGetAndSet("android.jpeg.thumbnailSize", Size.class, new Size(123, 456));
+
+        // int32 x 2 x n
+        checkKeyGetAndSetArray("android.scaler.availableJpegSizes", Size[].class, new Size[] {
+            new Size(123, 456),
+            new Size(0xDEAD, 0xF00D),
+            new Size(0xF00, 0xB00)
+        });
+    }
+
+    @SmallTest
+    public void testReadWriteRectangle() {
+        // int32 x n
+        checkKeyGetAndSet("android.scaler.cropRegion", Rect.class, new Rect(10, 11, 1280, 1024));
+
+        // int32 x 2 x n
+        checkKeyGetAndSetArray("android.statistics.faceRectangles", Rect[].class, new Rect[] {
+            new Rect(110, 111, 11280, 11024),
+            new Rect(210, 111, 21280, 21024),
+            new Rect(310, 111, 31280, 31024)
+        });
+    }
+
+    @SmallTest
+    public void testReadWriteString() {
+        // (byte) string
+        Key<String> gpsProcessingMethodKey =
+                new Key<String>("android.jpeg.gpsProcessingMethod", String.class);
+
+        String helloWorld = new String("HelloWorld");
+        byte[] helloWorldBytes = new byte[] {
+                'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0' };
+
+        mMetadata.set(gpsProcessingMethodKey, helloWorld);
+
+        String actual = mMetadata.get(gpsProcessingMethodKey);
+        assertEquals(helloWorld, actual);
+
+        byte[] actualBytes = mMetadata.readValues(getTag(gpsProcessingMethodKey.getName()));
+        assertArrayEquals(helloWorldBytes, actualBytes);
+
+        // Does not yet test as a string[] since we don't support that in native code.
+
+        // (byte) string
+        Key<String[]> gpsProcessingMethodKeyArray =
+                new Key<String[]>("android.jpeg.gpsProcessingMethod", String[].class);
+
+        String[] gpsStrings = new String[] { "HelloWorld", "FooBar", "Shazbot" };
+        byte[] gpsBytes = new byte[] {
+                'H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd', '\0',
+                'F', 'o', 'o', 'B', 'a', 'r', '\0',
+                'S', 'h', 'a', 'z', 'b', 'o', 't', '\0'};
+
+        mMetadata.set(gpsProcessingMethodKeyArray, gpsStrings);
+
+        String[] actualArray = mMetadata.get(gpsProcessingMethodKeyArray);
+        assertArrayEquals(gpsStrings, actualArray);
+
+        byte[] actualBytes2 = mMetadata.readValues(getTag(gpsProcessingMethodKeyArray.getName()));
+        assertArrayEquals(gpsBytes, actualBytes2);
+    }
+
+    <T> void compareGeneric(T expected, T actual) {
+        assertEquals(expected, actual);
     }
 }
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java
new file mode 100644
index 0000000..c15d030
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/RationalTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.mediaframeworktest.unit;
+
+import android.test.suitebuilder.annotation.SmallTest;
+import android.hardware.photography.Rational;
+
+/**
+ * <pre>
+ * adb shell am instrument \
+ *      -e class 'com.android.mediaframeworktest.unit.RationalTest' \
+ *      -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
+ * </pre>
+ */
+public class RationalTest extends junit.framework.TestCase {
+    @SmallTest
+    public void testConstructor() {
+
+        // Simple case
+        Rational r = new Rational(1, 2);
+        assertEquals(1, r.getNumerator());
+        assertEquals(2, r.getDenominator());
+
+        // Denominator negative
+        r = new Rational(-1, 2);
+        assertEquals(-1, r.getNumerator());
+        assertEquals(2, r.getDenominator());
+
+        // Numerator negative
+        r = new Rational(1, -2);
+        assertEquals(-1, r.getNumerator());
+        assertEquals(2, r.getDenominator());
+
+        // Both negative
+        r = new Rational(-1, -2);
+        assertEquals(1, r.getNumerator());
+        assertEquals(2, r.getDenominator());
+
+        // Dividing by zero is not allowed
+        try {
+            r = new Rational(1, 0);
+            fail("Expected Rational constructor to throw an IllegalArgumentException");
+        } catch(IllegalArgumentException e) {
+        }
+    }
+
+    @SmallTest
+    public void testGcd() {
+        Rational r = new Rational(1, 2);
+        assertEquals(1, r.gcd());
+
+        Rational twoThirds = new Rational(2, 3);
+        assertEquals(1, twoThirds.gcd());
+
+        Rational moreComplicated2 = new Rational(5*78, 7*78);
+        assertEquals(78, moreComplicated2.gcd());
+
+        Rational oneHalf = new Rational(-1, 2);
+        assertEquals(1, oneHalf.gcd());
+
+        twoThirds = new Rational(-2, 3);
+        assertEquals(1, twoThirds.gcd());
+    }
+
+    @SmallTest
+    public void testEquals() {
+        Rational r = new Rational(1, 2);
+        assertEquals(1, r.getNumerator());
+        assertEquals(2, r.getDenominator());
+
+        assertEquals(r, r);
+        assertFalse(r.equals(null));
+        assertFalse(r.equals(new Object()));
+
+        Rational twoThirds = new Rational(2, 3);
+        assertFalse(r.equals(twoThirds));
+        assertFalse(twoThirds.equals(r));
+
+        Rational fourSixths = new Rational(4, 6);
+        assertEquals(twoThirds, fourSixths);
+        assertEquals(fourSixths, twoThirds);
+
+        Rational moreComplicated = new Rational(5*6*7*8*9, 1*2*3*4*5);
+        Rational moreComplicated2 = new Rational(5*6*7*8*9*78, 1*2*3*4*5*78);
+        assertEquals(moreComplicated, moreComplicated2);
+        assertEquals(moreComplicated2, moreComplicated);
+
+        // Ensure negatives are fine
+        twoThirds = new Rational(-2, 3);
+        fourSixths = new Rational(-4, 6);
+        assertEquals(twoThirds, fourSixths);
+        assertEquals(fourSixths, twoThirds);
+
+        moreComplicated = new Rational(-5*6*7*8*9, 1*2*3*4*5);
+        moreComplicated2 = new Rational(-5*6*7*8*9*78, 1*2*3*4*5*78);
+        assertEquals(moreComplicated, moreComplicated2);
+        assertEquals(moreComplicated2, moreComplicated);
+
+    }
+}
\ No newline at end of file
diff --git a/packages/BackupRestoreConfirmation/res/values-nl/strings.xml b/packages/BackupRestoreConfirmation/res/values-nl/strings.xml
index d525429..7bb0e9b 100644
--- a/packages/BackupRestoreConfirmation/res/values-nl/strings.xml
+++ b/packages/BackupRestoreConfirmation/res/values-nl/strings.xml
@@ -26,9 +26,9 @@
     <string name="deny_restore_button_label" msgid="1724367334453104378">"Niet herstellen"</string>
     <string name="current_password_text" msgid="8268189555578298067">"Geef hieronder uw huidige back-upwachtwoord op:"</string>
     <string name="device_encryption_restore_text" msgid="1570864916855208992">"Geef hieronder uw wachtwoord voor apparaatcodering op."</string>
-    <string name="device_encryption_backup_text" msgid="5866590762672844664">"Geef hieronder uw wachtwoord voor apparaatcodering op. Dit wordt ook gebruikt om het back-uparchief te coderen."</string>
+    <string name="device_encryption_backup_text" msgid="5866590762672844664">"Geef hieronder uw wachtwoord voor apparaatversleuteling op. Dit wordt ook gebruikt om het back-uparchief te versleutelen."</string>
     <string name="backup_enc_password_text" msgid="4981585714795233099">"Geef een wachtwoord op dat u wilt gebruiken voor het coderen van de gegevens van de volledige back-up. Als u dit leeg laat, wordt uw huidige back-upwachtwoord gebruikt:"</string>
-    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Als u de gegevens van de volledige back-up wilt coderen, geeft u daarvoor hieronder een wachtwoord op:"</string>
+    <string name="backup_enc_password_optional" msgid="1350137345907579306">"Als u de gegevens van de volledige back-up wilt versleutelen, geeft u daarvoor hieronder een wachtwoord op:"</string>
     <string name="restore_enc_password_text" msgid="6140898525580710823">"Als deze herstelgegevens zijn gecodeerd, geeft u hieronder het wachtwoord op:"</string>
     <string name="toast_backup_started" msgid="550354281452756121">"Back-up starten..."</string>
     <string name="toast_backup_ended" msgid="3818080769548726424">"Back-up voltooid"</string>
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 58fc8e8..6343d0a 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -71,7 +71,7 @@
 import libcore.io.IoUtils;
 import libcore.io.Libcore;
 import libcore.io.Streams;
-import libcore.io.StructStatFs;
+import libcore.io.StructStatVfs;
 
 /*
  * This service copies a downloaded apk to a file passed in as
@@ -246,7 +246,7 @@
             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
 
             try {
-                final StructStatFs stat = Libcore.os.statfs(path);
+                final StructStatVfs stat = Libcore.os.statvfs(path);
                 final long totalSize = stat.f_blocks * stat.f_bsize;
                 final long availSize = stat.f_bavail * stat.f_bsize;
                 return new long[] { totalSize, availSize };
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/Keyguard/res/values-am/strings.xml b/packages/Keyguard/res/values-am/strings.xml
index 325163d..fc9d946 100644
--- a/packages/Keyguard/res/values-am/strings.xml
+++ b/packages/Keyguard/res/values-am/strings.xml
@@ -85,7 +85,7 @@
     <string name="description_target_camera" msgid="969071997552486814">"ካሜራ"</string>
     <string name="description_target_silent" msgid="893551287746522182">"ፀጥታ"</string>
     <string name="description_target_soundon" msgid="30052466675500172">"ድምፅ አብራ"</string>
-    <string name="description_target_search" msgid="3091587249776033139">"ፈልግ"</string>
+    <string name="description_target_search" msgid="3091587249776033139">"ፍለጋ"</string>
     <string name="description_direction_up" msgid="7169032478259485180">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ላይ አንሸራትት።"</string>
     <string name="description_direction_down" msgid="5087739728639014595">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ታች አንሸራትት።"</string>
     <string name="description_direction_left" msgid="7207478719805562165">"ለ<xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ወደ ግራ አንሸራትት።"</string>
diff --git a/packages/Keyguard/res/values-hdpi/dimens.xml b/packages/Keyguard/res/values-hdpi/dimens.xml
new file mode 100644
index 0000000..2c209e2
--- /dev/null
+++ b/packages/Keyguard/res/values-hdpi/dimens.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/dimens.xml
+**
+** Copyright 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.
+*/
+-->
+<resources>
+
+    <!-- Height of the sliding KeyguardSecurityContainer (includes 2x keyguard_security_view_margin) -->
+    <dimen name="keyguard_security_height">345dp</dimen>
+
+</resources>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
index 714dfbd..893562e 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -163,7 +163,8 @@
                 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                 case KeyEvent.KEYCODE_MEDIA_REWIND:
                 case KeyEvent.KEYCODE_MEDIA_RECORD:
-                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
                     handleMediaKeyEvent(event);
                     return true;
                 }
@@ -204,7 +205,8 @@
                 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                 case KeyEvent.KEYCODE_MEDIA_REWIND:
                 case KeyEvent.KEYCODE_MEDIA_RECORD:
-                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
                     handleMediaKeyEvent(event);
                     return true;
                 }
diff --git a/packages/PrintSpooler/Android.mk b/packages/PrintSpooler/Android.mk
index a68fcdf..8ae0302 100644
--- a/packages/PrintSpooler/Android.mk
+++ b/packages/PrintSpooler/Android.mk
@@ -22,7 +22,7 @@
 
 LOCAL_PACKAGE_NAME := PrintSpooler
 
-LOCAL_JAVA_LIBRARIES := framework
+LOCAL_JAVA_LIBRARIES := framework-base
 
 LOCAL_CERTIFICATE := platform
 
diff --git a/packages/PrintSpooler/AndroidManifest.xml b/packages/PrintSpooler/AndroidManifest.xml
index fbb0060..f0bbfa4 100644
--- a/packages/PrintSpooler/AndroidManifest.xml
+++ b/packages/PrintSpooler/AndroidManifest.xml
@@ -46,7 +46,8 @@
 
         <activity
             android:name=".PrintJobConfigActivity"
-            android:exported="true">
+            android:exported="true"
+            android:theme="@android:style/Theme.Holo.Light.Dialog.NoActionBar">
         </activity>
 
     </application>
diff --git a/packages/PrintSpooler/res/layout/print_job_config_activity.xml b/packages/PrintSpooler/res/layout/print_job_config_activity.xml
index 51e425d..8736bdd 100644
--- a/packages/PrintSpooler/res/layout/print_job_config_activity.xml
+++ b/packages/PrintSpooler/res/layout/print_job_config_activity.xml
@@ -14,248 +14,197 @@
      limitations under the License.
 -->
 
-<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
-    android:layout_height="wrap_content">
+    android:layout_height="wrap_content"
+    android:orientation="vertical">
 
-    <GridLayout
-        android:layout_width="wrap_content"
+    <ScrollView
+        android:layout_width="fill_parent"
         android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:orientation="vertical"
-        android:columnCount="2">
+        android:background="@*android:color/bright_foreground_disabled_holo_light">
 
-        <EditText
-            android:id="@+id/copies_edittext"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="0"
-            android:layout_column="1"
-            android:minWidth="150dip"
-            android:inputType="number"
-            android:selectAllOnFocus="true">
-        </EditText>
-
-        <TextView
+        <GridLayout
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="0"
-            android:layout_column="0"
-            android:text="@string/label_copies"
-            android:textAppearance="?android:attr/textAppearanceMedium"
-            android:labelFor="@id/copies_edittext">
-        </TextView>
+            android:layout_margin="32dip"
+            android:orientation="vertical"
+            android:columnCount="2">
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="1"
-            android:layout_column="0"
-            android:text="@string/label_destination"
-            android:textAppearance="?android:attr/textAppearanceMedium">
-        </TextView>
+            <!-- Destination -->
 
-        <Spinner
-            android:id="@+id/destination_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="1"
-            android:layout_column="1"
-            android:minWidth="150dip">
-        </Spinner>
+            <Spinner
+                android:id="@+id/destination_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginBottom="12dip"
+                android:layout_row="0"
+                android:layout_column="0"
+                android:layout_columnSpan="2"
+                android:minWidth="324dip"
+                android:minHeight="?android:attr/listPreferredItemHeightSmall">
+            </Spinner>
 
+            <!-- Copies -->
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="2"
-            android:layout_column="0"
-            android:text="@string/label_media_size"
-            android:textAppearance="?android:attr/textAppearanceMedium">
-        </TextView>
+            <view
+                class="com.android.printspooler.PrintJobConfigActivity$CustomEditText"
+                android:id="@+id/copies_edittext"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="12dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="2"
+                android:layout_column="0"
+                android:layout_gravity="bottom"
+                android:inputType="numberDecimal"
+                android:selectAllOnFocus="true"
+                android:minWidth="150dip">
+            </view>
 
-        <Spinner
-            android:id="@+id/media_size_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="2"
-            android:layout_column="1"
-            android:minWidth="150dip">
-        </Spinner>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="12dip"
+                android:layout_marginRight="12dip"
+                android:layout_row="1"
+                android:layout_column="0"
+                android:layout_gravity="left|bottom"
+                android:text="@string/label_copies"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/copies_edittext">
+            </TextView>
 
+            <!-- Paper size -->
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="3"
-            android:layout_column="0"
-            android:text="@string/label_resolution"
-            android:textAppearance="?android:attr/textAppearanceMedium">
-        </TextView>
+            <Spinner
+                android:id="@+id/paper_size_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="2"
+                android:layout_column="1"
+                android:minWidth="150dip">
+            </Spinner>
 
-        <Spinner
-            android:id="@+id/resolution_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="3"
-            android:layout_column="1"
-            android:minWidth="150dip">
-        </Spinner>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginTop="12dip"
+                android:layout_row="1"
+                android:layout_column="1"
+                android:text="@string/label_paper_size"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/paper_size_spinner">
+            </TextView>
 
+            <!-- Color -->
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="4"
-            android:layout_column="0"
-            android:text="@string/label_input_tray"
-            android:textAppearance="?android:attr/textAppearanceMedium">
-        </TextView>
+            <Spinner
+                android:id="@+id/color_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="12dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="4"
+                android:layout_column="0"
+                android:minWidth="150dip">
+            </Spinner>
 
-        <Spinner
-            android:id="@+id/input_tray_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="4"
-            android:layout_column="1"
-            android:minWidth="150dip">
-        </Spinner>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="12dip"
+                android:layout_marginRight="12dip"
+                android:layout_row="3"
+                android:layout_column="0"
+                android:text="@string/label_color"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/color_spinner">
+            </TextView>
 
+            <!-- Orientation -->
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="5"
-            android:layout_column="0"
-            android:text="@string/label_output_tray"
-            android:textAppearance="?android:attr/textAppearanceMedium">
-        </TextView>
+            <Spinner
+                android:id="@+id/orientation_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginBottom="12dip"
+                android:layout_row="4"
+                android:layout_column="1"
+                android:minWidth="150dip">
+            </Spinner>
 
-        <Spinner
-            android:id="@+id/output_tray_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="5"
-            android:layout_column="1"
-            android:minWidth="150dip">
-        </Spinner>
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_marginTop="12dip"
+                android:layout_row="3"
+                android:layout_column="1"
+                android:text="@string/label_orientation"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/orientation_spinner">
+            </TextView>
 
+            <!-- Pages -->
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="6"
-            android:layout_column="0"
-            android:text="@string/label_duplex_mode"
-            android:textAppearance="?android:attr/textAppearanceMedium">
-        </TextView>
+            <Spinner
+                android:id="@+id/range_options_spinner"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginRight="12dip"
+                android:layout_row="6"
+                android:layout_column="0"
+                android:minWidth="150dip">
+            </Spinner>
 
-        <Spinner
-            android:id="@+id/duplex_mode_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="6"
-            android:layout_column="1"
-            android:minWidth="150dip">
-        </Spinner>
+            <EditText
+                android:id="@+id/page_range_edittext"
+                android:layout_width="fill_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="12dip"
+                android:layout_row="6"
+                android:layout_column="1"
+                android:layout_gravity="bottom"
+                android:selectAllOnFocus="true"
+                android:minWidth="150dip"
+                android:hint="@string/pages_range_example"
+                android:inputType="textNoSuggestions"
+                android:visibility="gone">
+            </EditText>
 
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="12dip"
+                android:layout_marginRight="12dip"
+                android:layout_row="5"
+                android:layout_column="0"
+                android:text="@string/label_pages"
+                android:textAppearance="?android:attr/textAppearanceSmall"
+                android:textStyle="bold"
+                android:labelFor="@id/range_options_spinner">
+            </TextView>
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="7"
-            android:layout_column="0"
-            android:text="@string/label_color_mode"
-            android:textAppearance="?android:attr/textAppearanceMedium">
-        </TextView>
+        </GridLayout>
 
-        <Spinner
-            android:id="@+id/color_mode_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="7"
-            android:layout_column="1"
-            android:minWidth="150dip">
-        </Spinner>
+    </ScrollView>
 
+    <Button
+        android:id="@+id/print_button"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:padding="0dip"
+        android:text="@string/print_button"
+        android:background="?android:attr/selectableItemBackground">
+    </Button>
 
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="8"
-            android:layout_column="0"
-            android:text="@string/label_fitting_mode"
-            android:textAppearance="?android:attr/textAppearanceMedium">
-        </TextView>
-
-        <Spinner
-            android:id="@+id/fitting_mode_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="8"
-            android:layout_column="1"
-            android:minWidth="150dip">
-        </Spinner>
-
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="9"
-            android:layout_column="0"
-            android:text="@string/label_orientation"
-            android:textAppearance="?android:attr/textAppearanceMedium">
-        </TextView>
-
-        <Spinner
-            android:id="@+id/orientation_spinner"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-            android:layout_marginLeft="12dip"
-            android:layout_marginRight="12dip"
-            android:layout_row="9"
-            android:layout_column="1"
-            android:minWidth="150dip">
-        </Spinner>
-
-    </GridLayout>
-
-</ScrollView>
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
new file mode 100644
index 0000000..66c6724
--- /dev/null
+++ b/packages/PrintSpooler/res/layout/spinner_dropdown_item.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<LinearLayout  xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingLeft="8dip"
+    android:paddingRight="8dip"
+    android:minHeight="?android:attr/listPreferredItemHeightSmall"
+    android:orientation="vertical"
+    android:gravity="center_vertical">
+
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:singleLine="true"
+        android:ellipsize="end"
+        android:textIsSelectable="false">
+    </TextView>
+
+    <TextView
+        android:id="@+id/subtitle"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        android:singleLine="true"
+        android:ellipsize="end"
+        android:textIsSelectable="false"
+        android:visibility="gone">
+
+    </TextView>
+
+</LinearLayout>
diff --git a/packages/PrintSpooler/res/menu/print_job_config_activity.xml b/packages/PrintSpooler/res/values/constants.xml
similarity index 65%
rename from packages/PrintSpooler/res/menu/print_job_config_activity.xml
rename to packages/PrintSpooler/res/values/constants.xml
index 149c274..7d2cdc3 100644
--- a/packages/PrintSpooler/res/menu/print_job_config_activity.xml
+++ b/packages/PrintSpooler/res/values/constants.xml
@@ -13,9 +13,15 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item android:id="@+id/print_button"
-          android:title="@string/print_button"
-          android:showAsAction="ifRoom">
-    </item>
-</menu>
+
+<resources>
+
+    <integer name="page_option_value_all">1</integer>
+    <integer name="page_option_value_page_range">2</integer>
+
+    <integer-array name="page_options_values" translatable="false">
+        <item>@integer/page_option_value_all</item>
+        <item>@integer/page_option_value_page_range</item>
+    </integer-array>
+
+</resources>
\ No newline at end of file
diff --git a/packages/PrintSpooler/res/values/strings.xml b/packages/PrintSpooler/res/values/strings.xml
index 8b4b40a..de6fb60 100644
--- a/packages/PrintSpooler/res/values/strings.xml
+++ b/packages/PrintSpooler/res/values/strings.xml
@@ -16,74 +16,44 @@
 
 <resources>
 
-    <!-- Title of the PrintSpooler application. [CHAR LIMIT=16] -->
+    <!-- Title of the PrintSpooler application. [CHAR LIMIT=50] -->
     <string name="app_label">Print Spooler</string>
 
-    <!-- Title of the print dialog. [CHAR LIMIT=10] -->
-    <string name="print_job_config_dialog_title">Print</string>
-
     <!-- Label of the print dialog's print button. [CHAR LIMIT=16] -->
-    <string name="print_button">Print</string>
+    <string name="print_button">PRINT</string>
 
-    <!-- Label of the print dialog's cancel button. [CHAR LIMIT=16] -->
-    <string name="cancel_button">Cancel</string>
+    <!-- Label of the destination widget. [CHAR LIMIT=20] -->
+    <string name="label_destination">DESTIINATION</string>
 
-    <!-- Label of the destination spinner. [CHAR LIMIT=16] -->
-    <string name="label_destination">Destination</string>
+    <!-- Label of the copies count widget. [CHAR LIMIT=20] -->
+    <string name="label_copies">COPIES</string>
 
-    <!-- Label of the copies count edit text. [CHAR LIMIT=16] -->
-    <string name="label_copies">Copies</string>
+    <!-- Label of the paper size widget. [CHAR LIMIT=20] -->
+    <string name="label_paper_size">PAPER SIZE</string>
 
-    <!-- Label of the media size spinner. [CHAR LIMIT=16] -->
-    <string name="label_media_size">Media size</string>
+    <!-- Label of the color mode widget. [CHAR LIMIT=20] -->
+    <string name="label_color">COLOR</string>
 
-    <!-- Label of the resolution spinner. [CHAR LIMIT=16] -->
-    <string name="label_resolution">Resolution</string>
+    <!-- Label of the orientation widget. [CHAR LIMIT=20] -->
+    <string name="label_orientation">ORIENTATION</string>
 
-    <!-- Label of the input tray spinner. [CHAR LIMIT=16] -->
-    <string name="label_input_tray">Input tray</string>
+    <!-- Label of the page selection widget. [CHAR LIMIT=20] -->
+    <string name="label_pages">PAGES</string>
 
-    <!-- Label of the output tray spinner. [CHAR LIMIT=16] -->
-    <string name="label_output_tray">Output tray</string>
+    <!-- Page range exmple used as a hint of how to specify such. [CHAR LIMIT=15] -->
+    <string name="pages_range_example">e.g. 1&#8211;5, 8</string>
 
-    <!-- Label of the duplex mode spinner. [CHAR LIMIT=16] -->
-    <string name="label_duplex_mode">Duplex mode</string>
-
-    <!-- Label of the color mode spinner. [CHAR LIMIT=16] -->
-    <string name="label_color_mode">Color mode</string>
-
-    <!-- Label of the fitting mode spinner. [CHAR LIMIT=16] -->
-    <string name="label_fitting_mode">Fitting mode</string>
-
-    <!-- Label of the orientation spinner. [CHAR LIMIT=16] -->
-    <string name="label_orientation">Orientation</string>
-
-    <!-- Duplex mode labels. -->
-    <string-array name="duplex_mode_labels">
-        <!-- Duplex mode label: No duplexing. [CHAR LIMIT=20] -->
-        <item>None</item>
-        <!-- Duplex mode label: Turn a page along its long edge, e.g. like a book. [CHAR LIMIT=20] -->
-        <item>Long edge</item>
-        <!-- Duplex mode label: Turn a page along its short edge, e.g. like a notepad. [CHAR LIMIT=20] -->
-        <item>Short edge</item>
-    </string-array>
+    <!-- Message to notify the user of entering invalid input. [CHAR LIMIT=25] -->
+    <string name="invalid_input">Invalid input</string>
 
     <!-- Color mode labels. -->
     <string-array name="color_mode_labels">
         <!-- Color modelabel: Monochrome color scheme, e.g. one color is used. [CHAR LIMIT=20] -->
-        <item>Monochrome</item>
+        <item>Black &amp; White</item>
         <!-- Color mode label: Color color scheme, e.g. many colors are used. [CHAR LIMIT=20] -->
         <item>Color</item>
     </string-array>
 
-    <!-- Fitting mode labels. -->
-    <string-array name="fitting_mode_labels">
-        <!-- Fitting mode label: No fitting. [CHAR LIMIT=30] -->
-        <item>None</item>
-        <!-- Fitting mode label: Fit the content to the page. [CHAR LIMIT=30] -->
-        <item>Fit to page</item>
-    </string-array>
-
     <!-- Orientation labels. -->
     <string-array name="orientation_labels">
         <!-- Orientation label: Portrait page orientation. [CHAR LIMIT=30] -->
@@ -92,6 +62,14 @@
         <item>Landscape</item>
     </string-array>
 
+    <!-- Page options labels. -->
+    <string-array name="page_options_labels">
+        <!-- Page range option label: Print all pages [CHAR LIMIT=30] -->
+        <item>All</item>
+        <!-- Page range option label: Print a page range [CHAR LIMIT=30] -->
+        <item>Range</item>
+    </string-array>
+
     <!-- Title of an application permission, listed so the user can choose
          whether they want to allow the application to do this. -->
     <string name="permlab_bindPrintSpoolerService">bind to a print spooler service</string>
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
index ae2fe5c..19f545d 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintJobConfigActivity.java
@@ -17,50 +17,57 @@
 package com.android.printspooler;
 
 import android.app.Activity;
+import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.graphics.Rect;
 import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Binder;
 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;
 import android.text.Editable;
-import android.text.InputFilter;
-import android.text.Spanned;
 import android.text.TextUtils;
 import android.text.TextWatcher;
+import android.util.AttributeSet;
 import android.util.Log;
-import android.view.Menu;
-import android.view.MenuItem;
 import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
 import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemSelectedListener;
 import android.widget.ArrayAdapter;
+import android.widget.Button;
 import android.widget.EditText;
 import android.widget.Spinner;
+import android.widget.TextView;
 
 import java.io.File;
-import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  * Activity for configuring a print job.
@@ -78,9 +85,15 @@
 
     private static final int MIN_COPIES = 1;
 
-    private final List<QueuedAsyncTask<?>> mTaskQueue = new ArrayList<QueuedAsyncTask<?>>();
+    private static final Pattern PATTERN_DIGITS = Pattern.compile("\\d");
 
-    private IPrintManager mPrintManager;
+    private static final Pattern PATTERN_ESCAPE_SPECIAL_CHARS = Pattern.compile(
+            "(?=[]\\[+&|!(){}^\"~*?:\\\\])");
+
+    private static final Pattern PATTERN_PAGE_RANGE = Pattern.compile(
+            "([0-9]+[\\s]*[\\-]?[\\s]*[0-9]*[\\s]*[,]?[\\s]*)+");
+
+    private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
 
     private IPrinterDiscoveryObserver mPrinterDiscoveryObserver;
 
@@ -89,46 +102,38 @@
 
     private PrintAttributes mPrintAttributes;
 
-    private final PrintSpooler mPrintSpooler = PrintSpooler.getInstance(this);
+    private RemotePrintDocumentAdapter mRemotePrintAdapter;
 
-    private RemotePrintAdapter mRemotePrintAdapter;
+    private boolean mPrintConfirmed;
+
+    private boolean mStarted;
+
+    private IBinder mIPrintDocumentAdapter;
+
+    private PrintDocumentInfo mPrintDocumentInfo;
 
     // UI elements
 
     private EditText mCopiesEditText;
 
+    private EditText mRangeEditText;
+
     private Spinner mDestinationSpinner;
     public ArrayAdapter<SpinnerItem<PrinterInfo>> mDestinationSpinnerAdapter;
 
     private Spinner mMediaSizeSpinner;
     public ArrayAdapter<SpinnerItem<MediaSize>> mMediaSizeSpinnerAdapter;
 
-    private Spinner mResolutionSpinner;
-    public ArrayAdapter<SpinnerItem<Resolution>> mResolutionSpinnerAdapter;
-
-    private Spinner mInputTraySpinner;
-    public ArrayAdapter<SpinnerItem<Tray>> mInputTraySpinnerAdapter;
-
-    private Spinner mOutputTraySpinner;
-    public ArrayAdapter<SpinnerItem<Tray>> mOutputTraySpinnerAdapter;
-
-    private Spinner mDuplexModeSpinner;
-    public ArrayAdapter<SpinnerItem<Integer>> mDuplexModeSpinnerAdapter;
-
     private Spinner mColorModeSpinner;
     public ArrayAdapter<SpinnerItem<Integer>> mColorModeSpinnerAdapter;
 
-    private Spinner mFittingModeSpinner;
-    public ArrayAdapter<SpinnerItem<Integer>> mFittingModeSpinnerAdapter;
-
     private Spinner mOrientationSpinner;
     public ArrayAdapter<SpinnerItem<Integer>> mOrientationSpinnerAdapter;
 
-    private boolean mPrintStarted;
+    private Spinner mRangeOptionsSpinner;
+    public ArrayAdapter<SpinnerItem<Integer>> mRangeOptionsSpinnerAdapter;
 
-    private boolean mPrintConfirmed;
-
-    private IBinder mPrinable;
+    private Button mPrintButton;
 
     // TODO: Implement store/restore state.
 
@@ -143,35 +148,34 @@
                 SpinnerItem<MediaSize> mediaItem = mMediaSizeSpinnerAdapter.getItem(position);
                 mPrintAttributes.setMediaSize(mediaItem.value);
                 updatePrintableContentIfNeeded();
-            } else if (spinner == mResolutionSpinner) {
-                SpinnerItem<Resolution> resolutionItem =
-                        mResolutionSpinnerAdapter.getItem(position);
-                mPrintAttributes.setResolution(resolutionItem.value);
-                updatePrintableContentIfNeeded();
-            } else if (spinner == mInputTraySpinner) {
-                SpinnerItem<Tray> inputTrayItem =
-                        mInputTraySpinnerAdapter.getItem(position);
-                mPrintAttributes.setInputTray(inputTrayItem.value);
-            } else if (spinner == mOutputTraySpinner) {
-                SpinnerItem<Tray> outputTrayItem =
-                        mOutputTraySpinnerAdapter.getItem(position);
-                mPrintAttributes.setOutputTray(outputTrayItem.value);
-            } else if (spinner == mDuplexModeSpinner) {
-                SpinnerItem<Integer> duplexModeItem =
-                        mDuplexModeSpinnerAdapter.getItem(position);
-                mPrintAttributes.setDuplexMode(duplexModeItem.value);
             } else if (spinner == mColorModeSpinner) {
                 SpinnerItem<Integer> colorModeItem =
                         mColorModeSpinnerAdapter.getItem(position);
                 mPrintAttributes.setColorMode(colorModeItem.value);
-            } else if (spinner == mFittingModeSpinner) {
-                SpinnerItem<Integer> fittingModeItem =
-                        mFittingModeSpinnerAdapter.getItem(position);
-                mPrintAttributes.setFittingMode(fittingModeItem.value);
             } else if (spinner == mOrientationSpinner) {
                 SpinnerItem<Integer> orientationItem =
                         mOrientationSpinnerAdapter.getItem(position);
                 mPrintAttributes.setOrientation(orientationItem.value);
+            } else if (spinner == mRangeOptionsSpinner) {
+                SpinnerItem<Integer> rangeOptionItem =
+                        mRangeOptionsSpinnerAdapter.getItem(position);
+                if (rangeOptionItem.value == getResources().getInteger(
+                        R.integer.page_option_value_all)) {
+                    mRangeEditText.setVisibility(View.INVISIBLE);
+                    mRangeEditText.setEnabled(false);
+                    mRangeEditText.setText(null);
+                    mRangeEditText.setError(null);
+                    mPrintButton.setEnabled(true);
+                } else if (rangeOptionItem.value ==  getResources().getInteger(
+                        R.integer.page_option_value_page_range)) {
+                    mRangeEditText.setVisibility(View.VISIBLE);
+                    mRangeEditText.setEnabled(true);
+                    mRangeEditText.requestFocus();
+                    mRangeEditText.setError(getString(R.string.invalid_input));
+                    InputMethodManager imm = (InputMethodManager)
+                            getSystemService(INPUT_METHOD_SERVICE);
+                    imm.showSoftInput(mRangeEditText, 0);
+                }
             }
         }
 
@@ -181,11 +185,10 @@
         }
     };
 
-    private final TextWatcher mTextWatcher = new TextWatcher() {
+    private final TextWatcher mCopiesTextWatcher = new TextWatcher() {
         @Override
         public void onTextChanged(CharSequence s, int start, int before, int count) {
-            final int copies = Integer.parseInt(mCopiesEditText.getText().toString());
-            mPrintAttributes.setCopies(copies);
+            /* do nothing */
         }
 
         @Override
@@ -194,25 +197,64 @@
         }
 
         @Override
-        public void afterTextChanged(Editable s) {
-            /* do nothing */
+        public void afterTextChanged(Editable editable) {
+            if (editable.length() == 0) {
+                mCopiesEditText.setError(getString(R.string.invalid_input));
+                mPrintButton.setEnabled(false);
+                return;
+            }
+            final int copies = Integer.parseInt(editable.toString());
+            if (copies < MIN_COPIES) {
+                mCopiesEditText.setError(getString(R.string.invalid_input));
+                mPrintButton.setEnabled(false);
+                return;
+            }
+            mPrintAttributes.setCopies(copies);
+            mPrintButton.setEnabled(true);
         }
     };
 
-    private final InputFilter mInputFilter = new InputFilter() {
+    private final TextWatcher mRangeTextWatcher = new TextWatcher() {
         @Override
-        public CharSequence filter(CharSequence source, int start, int end,
-                Spanned dest, int dstart, int dend) {
-            StringBuffer text = new StringBuffer(dest.toString());
-            text.replace(dstart, dend, source.subSequence(start, end).toString());
+        public void onTextChanged(CharSequence s, int start, int before, int count) {
+            /* do nothing */
+        }
+
+        @Override
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+            /* do nothing */
+        }
+
+        @Override
+        public void afterTextChanged(Editable editable) {
+            String text = editable.toString();
+
             if (TextUtils.isEmpty(text)) {
-                return dest;
+                mRangeEditText.setError(getString(R.string.invalid_input));
+                mPrintButton.setEnabled(false);
+                return;
             }
-            final int copies = Integer.parseInt(text.toString());
-            if (copies < MIN_COPIES) {
-                return dest;
+
+            String escapedText = PATTERN_ESCAPE_SPECIAL_CHARS.matcher(text).replaceAll("////");
+            if (!PATTERN_PAGE_RANGE.matcher(escapedText).matches()) {
+                mRangeEditText.setError(getString(R.string.invalid_input));
+                mPrintButton.setEnabled(false);
+                return;
             }
-            return null;
+
+            Matcher matcher = PATTERN_DIGITS.matcher(text);
+            while (matcher.find()) {
+                String numericString = text.substring(matcher.start(), matcher.end());
+                final int pageIndex = Integer.parseInt(numericString);
+                if (pageIndex < 1 || pageIndex > mPrintDocumentInfo.getPageCount()) {
+                    mRangeEditText.setError(getString(R.string.invalid_input));
+                    mPrintButton.setEnabled(false);
+                    return;
+                }
+            }
+
+            mRangeEditText.setError(null);
+            mPrintButton.setEnabled(true);
         }
     };
 
@@ -231,9 +273,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 +290,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 +311,7 @@
 
     @Override
     protected void onDestroy() {
-        mPrinable.unlinkToDeath(mDeathRecipient, 0);
+        mIPrintDocumentAdapter.unlinkToDeath(mDeathRecipient, 0);
         super.onDestroy();
     }
 
@@ -279,70 +319,95 @@
         // Copies
         mCopiesEditText = (EditText) findViewById(R.id.copies_edittext);
         mCopiesEditText.setText(String.valueOf(MIN_COPIES));
-        mCopiesEditText.addTextChangedListener(mTextWatcher);
-        mCopiesEditText.setFilters(new InputFilter[] {mInputFilter});
+        mCopiesEditText.addTextChangedListener(mCopiesTextWatcher);
 
         // Destination.
         mDestinationSpinner = (Spinner) findViewById(R.id.destination_spinner);
         mDestinationSpinnerAdapter = new ArrayAdapter<SpinnerItem<PrinterInfo>>(this,
-                android.R.layout.simple_spinner_dropdown_item);
+                R.layout.spinner_dropdown_item) {
+                    @Override
+                    public View getDropDownView(int position, View convertView, ViewGroup parent) {
+                        return getView(position, convertView, parent);
+                    }
+
+                    @Override
+                    public View getView(int position, View convertView, ViewGroup parent) {
+                        if (convertView == null) {
+                            convertView = getLayoutInflater().inflate(
+                                    R.layout.spinner_dropdown_item, parent, false);
+                        }
+
+                        PrinterInfo printerInfo = getItem(position).value;
+                        TextView title = (TextView) convertView.findViewById(R.id.title);
+                        title.setText(printerInfo.getLabel());
+
+                        try {
+                            TextView subtitle = (TextView) convertView.findViewById(R.id.subtitle);
+                            PackageManager pm = getPackageManager();
+                            PackageInfo packageInfo = pm.getPackageInfo(
+                                    printerInfo.getId().getService().getPackageName(), 0);
+                            subtitle.setText(packageInfo.applicationInfo.loadLabel(pm));
+                            subtitle.setVisibility(View.VISIBLE);
+                        } catch (NameNotFoundException nnfe) {
+                            /* ignore */
+                        }
+
+                        return convertView;
+                    }
+        };
         mDestinationSpinner.setAdapter(mDestinationSpinnerAdapter);
         mDestinationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
 
         // Media size.
-        mMediaSizeSpinner = (Spinner) findViewById(R.id.media_size_spinner);
+        mMediaSizeSpinner = (Spinner) findViewById(R.id.paper_size_spinner);
         mMediaSizeSpinnerAdapter = new ArrayAdapter<SpinnerItem<MediaSize>>(this,
-                android.R.layout.simple_spinner_dropdown_item);
+                R.layout.spinner_dropdown_item, R.id.title);
         mMediaSizeSpinner.setAdapter(mMediaSizeSpinnerAdapter);
         mMediaSizeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
 
-        // Resolution.
-        mResolutionSpinner = (Spinner) findViewById(R.id.resolution_spinner);
-        mResolutionSpinnerAdapter = new ArrayAdapter<SpinnerItem<Resolution>>(this,
-                android.R.layout.simple_spinner_dropdown_item);
-        mResolutionSpinner.setAdapter(mResolutionSpinnerAdapter);
-        mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
-
-        // Input tray.
-        mInputTraySpinner = (Spinner) findViewById(R.id.input_tray_spinner);
-        mInputTraySpinnerAdapter = new ArrayAdapter<SpinnerItem<Tray>>(this,
-                android.R.layout.simple_spinner_dropdown_item);
-        mInputTraySpinner.setAdapter(mInputTraySpinnerAdapter);
-
-        // Output tray.
-        mOutputTraySpinner = (Spinner) findViewById(R.id.output_tray_spinner);
-        mOutputTraySpinnerAdapter = new ArrayAdapter<SpinnerItem<Tray>>(this,
-                android.R.layout.simple_spinner_dropdown_item);
-        mOutputTraySpinner.setAdapter(mOutputTraySpinnerAdapter);
-        mOutputTraySpinner.setOnItemSelectedListener(mOnItemSelectedListener);
-
-        // Duplex mode.
-        mDuplexModeSpinner = (Spinner) findViewById(R.id.duplex_mode_spinner);
-        mDuplexModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
-                android.R.layout.simple_spinner_dropdown_item);
-        mDuplexModeSpinner.setAdapter(mDuplexModeSpinnerAdapter);
-        mDuplexModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
-
         // Color mode.
-        mColorModeSpinner = (Spinner) findViewById(R.id.color_mode_spinner);
+        mColorModeSpinner = (Spinner) findViewById(R.id.color_spinner);
         mColorModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
-                android.R.layout.simple_spinner_dropdown_item);
+                R.layout.spinner_dropdown_item, R.id.title);
         mColorModeSpinner.setAdapter(mColorModeSpinnerAdapter);
         mColorModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
 
-        // Color mode.
-        mFittingModeSpinner = (Spinner) findViewById(R.id.fitting_mode_spinner);
-        mFittingModeSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
-                android.R.layout.simple_spinner_dropdown_item);
-        mFittingModeSpinner.setAdapter(mFittingModeSpinnerAdapter);
-        mFittingModeSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
-
         // Orientation
         mOrientationSpinner = (Spinner) findViewById(R.id.orientation_spinner);
         mOrientationSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
-                android.R.layout.simple_spinner_dropdown_item);
+                R.layout.spinner_dropdown_item, R.id.title);
         mOrientationSpinner.setAdapter(mOrientationSpinnerAdapter);
         mOrientationSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+
+        // Range
+        mRangeEditText = (EditText) findViewById(R.id.page_range_edittext);
+        mRangeEditText.addTextChangedListener(mRangeTextWatcher);
+
+        // Range options
+        mRangeOptionsSpinner = (Spinner) findViewById(R.id.range_options_spinner);
+        mRangeOptionsSpinnerAdapter = new ArrayAdapter<SpinnerItem<Integer>>(this,
+                R.layout.spinner_dropdown_item, R.id.title);
+        mRangeOptionsSpinner.setAdapter(mRangeOptionsSpinnerAdapter);
+        mRangeOptionsSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
+        final int[] rangeOptionsValues = getResources().getIntArray(
+                R.array.page_options_values);
+        String[] rangeOptionsLabels = getResources().getStringArray(
+                R.array.page_options_labels);
+        final int rangeOptionsCount = rangeOptionsLabels.length;
+        for (int i = 0; i < rangeOptionsCount; i++) {
+            mRangeOptionsSpinnerAdapter.add(new SpinnerItem<Integer>(
+                    rangeOptionsValues[i], rangeOptionsLabels[i]));
+        }
+        mRangeOptionsSpinner.setSelection(0);
+
+        mPrintButton = (Button) findViewById(R.id.print_button);
+        mPrintButton.setOnClickListener(new OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                mPrintConfirmed = true;
+                finish();
+            }
+        });
     }
 
     private void updateUi() {
@@ -353,6 +418,7 @@
         // Copies.
         mCopiesEditText.setText(String.valueOf(
                 Math.max(mPrintAttributes.getCopies(), MIN_COPIES)));
+        mCopiesEditText.selectAll();
 
         // Media size.
         mMediaSizeSpinnerAdapter.clear();
@@ -361,71 +427,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();
-        List<Resolution> resolutions = printer.getResolutions();
-        final int resolutionCount = resolutions.size();
-        for (int i = 0; i < resolutionCount; i++) {
-            Resolution resolution = resolutions.get(i);
-            mResolutionSpinnerAdapter.add(new SpinnerItem<Resolution>(
-                    resolution, resolution.getLabel(getPackageManager())));
-        }
-        final int selectedResolutionIndex = resolutions.indexOf(
-                mPrintAttributes.getResolution());
-        mResolutionSpinner.setOnItemSelectedListener(null);
-        mResolutionSpinner.setSelection(selectedResolutionIndex);
-        mResolutionSpinner.setOnItemSelectedListener(mOnItemSelectedListener);
-
-        // 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())));
-        }
-        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())));
-        }
-        final int selectedOutputTrayIndex = outputTrays.indexOf(
-                mPrintAttributes.getOutputTray());
-        mOutputTraySpinner.setSelection(selectedOutputTrayIndex);
-
-        // Duplex mode.
-        final int duplexModes = printer.getDuplexModes();
-        mDuplexModeSpinnerAdapter.clear();
-        String[] duplexModeLabels = getResources().getStringArray(
-                R.array.duplex_mode_labels);
-        int remainingDuplexModes = duplexModes;
-        while (remainingDuplexModes != 0) {
-            final int duplexBitOffset = Integer.numberOfTrailingZeros(remainingDuplexModes);
-            final int duplexMode = 1 << duplexBitOffset;
-            remainingDuplexModes &= ~duplexMode;
-            mDuplexModeSpinnerAdapter.add(new SpinnerItem<Integer>(duplexMode,
-                    duplexModeLabels[duplexBitOffset]));
-        }
-        final int selectedDuplexModeIndex = Integer.numberOfTrailingZeros(
-                (duplexModes & mPrintAttributes.getDuplexMode()));
-        mDuplexModeSpinner.setSelection(selectedDuplexModeIndex);
 
         // Color mode.
         final int colorModes = printer.getColorModes();
@@ -444,23 +451,6 @@
                 (colorModes & mPrintAttributes.getColorMode()));
         mColorModeSpinner.setSelection(selectedColorModeIndex);
 
-        // Fitting mode.
-        final int fittingModes = printer.getFittingModes();
-        mFittingModeSpinnerAdapter.clear();
-        String[] fittingModeLabels = getResources().getStringArray(
-                R.array.fitting_mode_labels);
-        int remainingFittingModes = fittingModes;
-        while (remainingFittingModes != 0) {
-            final int fittingBitOffset = Integer.numberOfTrailingZeros(remainingFittingModes);
-            final int fittingMode = 1 << fittingBitOffset;
-            remainingFittingModes &= ~fittingMode;
-            mFittingModeSpinnerAdapter.add(new SpinnerItem<Integer>(fittingMode,
-                    fittingModeLabels[fittingBitOffset]));
-        }
-        final int selectedFittingModeIndex = Integer.numberOfTrailingZeros(
-                (fittingModes & mPrintAttributes.getFittingMode()));
-        mFittingModeSpinner.setSelection(selectedFittingModeIndex);
-
         // Orientation.
         final int orientations = printer.getOrientations();
         mOrientationSpinnerAdapter.clear();
@@ -482,155 +472,96 @@
     @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();
     }
 
-    @Override
-    public boolean onCreateOptionsMenu(Menu menu) {
-        getMenuInflater().inflate(R.menu.print_job_config_activity, menu);
-        return true;
-    }
-
-    @Override
-    public boolean onOptionsItemSelected(MenuItem item) {
-        if (item.getItemId() == R.id.print_button) {
-            mPrintConfirmed = true;
-            finish();
-        }
-        return super.onOptionsItemSelected(item);
-    }
-
     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) {
+        // TODO: Implement setting the print preview attribute
+        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) {
+                mPrintDocumentInfo = info;
+
+                // 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);
+        }, new Bundle());
     }
 
     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.finish(!mPrintConfirmed);
+
+        // 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 +620,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;
@@ -791,4 +699,38 @@
             return label.toString();
         }
     }
+
+    /**
+     * An instance of this class class is intended to be the first focusable
+     * in a layout to which the system automatically gives focus. It performs
+     * some voodoo to avoid the first tap on it to start an edit mode, rather
+     * to bring up the IME, i.e. to get the behavior as if the view was not
+     * focused.
+     */
+    public static final class CustomEditText extends EditText {
+        private boolean mClickedBeforeFocus;
+
+        public CustomEditText(Context context, AttributeSet attrs) {
+            super(context, attrs);
+        }
+
+        @Override
+        public boolean performClick() {
+            super.performClick();
+            if (isFocused() && !mClickedBeforeFocus) {
+                clearFocus();
+                requestFocus();
+            }
+            mClickedBeforeFocus = true;
+            return true;
+        }
+
+        protected void onFocusChanged(boolean gainFocus, int direction,
+                Rect previouslyFocusedRect) {
+            if (!gainFocus) {
+                mClickedBeforeFocus = false;
+            }
+            super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);
+        }
+    }
 }
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
index 2b27b69..53ae1459 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/PrintSpooler.java
@@ -16,28 +16,21 @@
 
 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.PageRange;
 import android.print.PrintAttributes;
+import android.print.PrintAttributes.Margins;
+import android.print.PrintAttributes.MediaSize;
+import android.print.PrintAttributes.Resolution;
+import android.print.PrintAttributes.Tray;
+import android.print.PrintDocumentInfo;
 import android.print.PrintJobInfo;
 import android.print.PrintManager;
 import android.print.PrinterId;
@@ -48,15 +41,31 @@
 
 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();
 
     private static final boolean DEBUG_PRINT_JOB_LIFECYCLE = false;
 
-    private static final boolean DEBUG_PERSISTENCE = false;
+    private static final boolean DEBUG_PERSISTENCE = true;
 
-    private static final boolean PERSISTNECE_MANAGER_ENABLED = false;
+    private static final boolean PERSISTNECE_MANAGER_ENABLED = true;
 
     private static final String PRINT_FILE_EXTENSION = "pdf";
 
@@ -64,17 +73,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) {
@@ -87,13 +96,50 @@
 
     private PrintSpooler(Context context) {
         mContext = context;
-        mPersistanceManager = new PersistenceManager();
-        mPersistanceManager.readStateLocked();
-        mPrintManager = IPrintManager.Stub.asInterface(
-                ServiceManager.getService("print"));
+        mPersistanceManager = new PersistenceManager(context);
     }
 
-    public List<PrintJobInfo> getPrintJobs(ComponentName componentName, int state, int appId) {
+    public void setCleint(IPrintSpoolerClient client) {
+        synchronized (mLock) {
+            mClient = client;
+        }
+    }
+
+    public void restorePersistedState() {
+        synchronized (mLock) {
+            mPersistanceManager.readStateLocked();
+        }
+    }
+
+    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> getPrintJobInfos(ComponentName componentName, int state, int appId) {
         synchronized (mLock) {
             List<PrintJobInfo> foundPrintJobs = null;
             final int printJobCount = mPrintJobs.size();
@@ -102,11 +148,13 @@
                 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
-                        || state == printJob.getState();
+                final boolean sameState = (state == printJob.getState())
+                        || (state == PrintJobInfo.STATE_ANY)
+                        || (state == PrintJobInfo.STATE_ANY_VISIBLE_TO_CLIENTS
+                                && printJob.getState() > PrintJobInfo.STATE_CREATED);
                 if (sameComponent && sameAppId && sameState) {
                     if (foundPrintJobs == null) {
                         foundPrintJobs = new ArrayList<PrintJobInfo>();
@@ -118,7 +166,7 @@
         }
     }
 
-    public PrintJobInfo getPrintJob(int printJobId, int appId) {
+    public PrintJobInfo getPrintJobInfo(int printJobId, int appId) {
         synchronized (mLock) {
             final int printJobCount = mPrintJobs.size();
             for (int i = 0; i < printJobCount; i++) {
@@ -134,18 +182,13 @@
 
     public boolean cancelPrintJob(int printJobId, int appId) {
         synchronized (mLock) {
-            PrintJobInfo printJob = getPrintJob(printJobId, appId);
+            PrintJobInfo printJob = getPrintJobInfo(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;
@@ -161,14 +204,80 @@
             printJob.setAppId(appId);
             printJob.setLabel(label);
             printJob.setAttributes(attributes);
+            printJob.setState(PrintJobInfo.STATE_CREATED);
 
             addPrintJobLocked(printJob);
-            setPrintJobState(printJobId, PrintJobInfo.STATE_CREATED);
 
             return printJob;
         }
     }
 
+    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)) {
@@ -194,7 +303,7 @@
             FileInputStream in = null;
             FileOutputStream out = null;
             try {
-                PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+                PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
                 if (printJob != null) {
                     File file = generateFileForPrintJob(printJobId);
                     in = new FileInputStream(file);
@@ -213,24 +322,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,10 +353,21 @@
 
     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) {
-            PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+            if (mClient == null) {
+                throw new IllegalStateException("Client cannot be null.");
+            }
+            client = mClient;
+
+            PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
             if (printJob != null && printJob.getState() < state) {
                 success = true;
                 printJob.setState(state);
@@ -265,8 +375,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,20 +404,90 @@
         }
 
         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(final IPrintSpoolerClient client) {
+        // This has to run on the tread that is persisting the current state
+        // since this call may result in the system unbinding from the spooler
+        // and as a result the spooler process may get killed before the write
+        // completes.
+        new AsyncTask<Void, Void, Void>() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                try {
+                    client.onAllPrintJobsHandled();
+                } catch (RemoteException re) {
+                    Slog.e(LOG_TAG, "Error notify for all print job handled.", re);
+                }
+                return null;
+            }
+        }.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+    }
+
+    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);
+            PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
             if (printJob != null) {
                 printJob.setTag(tag);
                 mPersistanceManager.writeStateLocked();
@@ -302,9 +497,21 @@
         return false;
     }
 
+    public final boolean setPrintJobPrintDocumentInfo(int printJobId, PrintDocumentInfo info) {
+        synchronized (mLock) {
+            PrintJobInfo printJob = getPrintJobInfo(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);
+            PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
             if (printJob != null) {
                 printJob.setAttributes(attributes);
                 mPersistanceManager.writeStateLocked();
@@ -314,7 +521,7 @@
 
     public void setPrintJobPrinterId(int printJobId, PrinterId printerId) {
         synchronized (mLock) {
-            PrintJobInfo printJob = getPrintJob(printJobId, PrintManager.APP_ID_ANY);
+            PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
             if (printJob != null) {
                 printJob.setPrinterId(printerId);
                 mPersistanceManager.writeStateLocked();
@@ -322,40 +529,77 @@
         }
     }
 
+    public boolean setPrintJobPages(int printJobId, PageRange[] pages) {
+        synchronized (mLock) {
+            PrintJobInfo printJob = getPrintJobInfo(printJobId, PrintManager.APP_ID_ANY);
+            if (printJob != null) {
+                printJob.setPages(pages);
+                mPersistanceManager.writeStateLocked();
+                return true;
+            }
+        }
+        return false;
+    }
+
     private final class PersistenceManager {
         private static final String PERSIST_FILE_NAME = "print_spooler_state.xml";
 
         private static final String TAG_SPOOLER = "spooler";
         private static final String TAG_JOB = "job";
-        private static final String TAG_ID = "id";
-        private static final String TAG_TAG = "tag";
-        private static final String TAG_APP_ID = "app-id";
-        private static final String TAG_STATE = "state";
-        private static final String TAG_ATTRIBUTES = "attributes";
-        private static final String TAG_LABEL = "label";
-        private static final String TAG_PRINTER = "printer";
 
-        private static final String ATTRIBUTE_MEDIA_SIZE = "mediaSize";
-        private static final String ATTRIBUTE_RESOLUTION = "resolution";
-        private static final String ATTRIBUTE_MARGINS = "margins";
-        private static final String ATTRIBUTE_INPUT_TRAY = "inputTray";
-        private static final String ATTRIBUTE_OUTPUT_TRAY = "outputTray";
-        private static final String ATTRIBUTE_DUPLEX_MODE = "duplexMode";
-        private static final String ATTRIBUTE_COLOR_MODE = "colorMode";
-        private static final String ATTRIBUTE_FITTING_MODE = "fittingMode";
-        private static final String ATTRIBUTE_ORIENTATION = "orientation";
+        private static final String TAG_PRINTER_ID = "printerId";
+        private static final String TAG_PAGE_RANGE = "pageRange";
+        private static final String TAG_ATTRIBUTES = "attributes";
+        private static final String TAG_DOCUMENT_INFO = "documentInfo";
+
+        private static final String ATTR_ID = "id";
+        private static final String ATTR_LABEL = "label";
+        private static final String ATTR_STATE = "state";
+        private static final String ATTR_APP_ID = "appId";
+        private static final String ATTR_USER_ID = "userId";
+        private static final String ATTR_TAG = "tag";
+
+        private static final String TAG_MEDIA_SIZE = "mediaSize";
+        private static final String TAG_RESOLUTION = "resolution";
+        private static final String TAG_MARGINS = "margins";
+        private static final String TAG_INPUT_TRAY = "inputTray";
+        private static final String TAG_OUTPUT_TRAY = "outputTray";
+
+        private static final String ATTR_DUPLEX_MODE = "duplexMode";
+        private static final String ATTR_COLOR_MODE = "colorMode";
+        private static final String ATTR_FITTING_MODE = "fittingMode";
+        private static final String ATTR_ORIENTATION = "orientation";
+
+        private static final String ATTR_LOCAL_ID = "localId";
+        private static final String ATTR_SERVICE = "service";
+
+        private static final String ATTR_WIDTH_MILS = "widthMils";
+        private static final String ATTR_HEIGHT_MILS = "heightMils";
+
+        private static final String ATTR_HORIZONTAL_DPI = "horizontalDip";
+        private static final String ATTR_VERTICAL_DPI = "verticalDpi";
+
+        private static final String ATTR_LEFT_MILS = "leftMils";
+        private static final String ATTR_TOP_MILS = "topMils";
+        private static final String ATTR_RIGHT_MILS = "rightMils";
+        private static final String ATTR_BOTTOM_MILS = "bottomMils";
+
+        private static final String ATTR_START = "start";
+        private static final String ATTR_END = "end";
+
+        private static final String ATTR_PAGE_COUNT = "pageCount";
+        private static final String ATTR_CONTENT_TYPE = "contentType";
 
         private final AtomicFile mStatePersistFile;
 
         private boolean mWriteStateScheduled;
 
-        private PersistenceManager() {
-            mStatePersistFile = new AtomicFile(new File(mContext.getFilesDir(),
+        private PersistenceManager(Context context) {
+            mStatePersistFile = new AtomicFile(new File(context.getFilesDir(),
                     PERSIST_FILE_NAME));
         }
 
         public void writeStateLocked() {
-            // TODO: Implement persistence of PrintableInfo
             if (!PERSISTNECE_MANAGER_ENABLED) {
                 return;
             }
@@ -393,99 +637,134 @@
 
                     final int state = printJob.getState();
                     if (state < PrintJobInfo.STATE_QUEUED
-                            || state > PrintJobInfo.STATE_FAILED) {
+                            || state > PrintJobInfo.STATE_CANCELED) {
                         continue;
                     }
 
                     serializer.startTag(null, TAG_JOB);
 
-                    serializer.startTag(null, TAG_ID);
-                    serializer.text(String.valueOf(printJob.getId()));
-                    serializer.endTag(null, TAG_ID);
+                    serializer.attribute(null, ATTR_ID, String.valueOf(printJob.getId()));
+                    serializer.attribute(null, ATTR_LABEL, printJob.getLabel().toString());
+                    serializer.attribute(null, ATTR_STATE, String.valueOf(printJob.getState()));
+                    serializer.attribute(null, ATTR_APP_ID, String.valueOf(printJob.getAppId()));
+                    serializer.attribute(null, ATTR_USER_ID, String.valueOf(printJob.getUserId()));
+                    String tag = printJob.getTag();
+                    if (tag != null) {
+                        serializer.attribute(null, ATTR_TAG, tag);
+                    }
 
-                    serializer.startTag(null, TAG_TAG);
-                    serializer.text(printJob.getTag());
-                    serializer.endTag(null, TAG_TAG);
+                    PrinterId printerId = printJob.getPrinterId();
+                    if (printerId != null) {
+                        serializer.startTag(null, TAG_PRINTER_ID);
+                        serializer.attribute(null, ATTR_LOCAL_ID, printerId.getLocalId());
+                        serializer.attribute(null, ATTR_SERVICE, printerId.getService()
+                                .flattenToString());
+                        serializer.endTag(null, TAG_PRINTER_ID);
+                    }
 
-                    serializer.startTag(null, TAG_APP_ID);
-                    serializer.text(String.valueOf(printJob.getAppId()));
-                    serializer.endTag(null, TAG_APP_ID);
-
-                    serializer.startTag(null, TAG_LABEL);
-                    serializer.text(printJob.getLabel().toString());
-                    serializer.endTag(null, TAG_LABEL);
-
-                    serializer.startTag(null, TAG_STATE);
-                    serializer.text(String.valueOf(printJob.getState()));
-                    serializer.endTag(null, TAG_STATE);
-
-                    serializer.startTag(null, TAG_PRINTER);
-                    serializer.text(printJob.getPrinterId().flattenToString());
-                    serializer.endTag(null, TAG_PRINTER);
+                    PageRange[] pages = printJob.getPages();
+                    if (pages != null) {
+                        for (int i = 0; i < pages.length; i++) {
+                            serializer.startTag(null, TAG_PAGE_RANGE);
+                            serializer.attribute(null, ATTR_START, String.valueOf(
+                                    pages[i].getStart()));
+                            serializer.attribute(null, ATTR_END, String.valueOf(
+                                    pages[i].getEnd()));
+                            serializer.endTag(null, TAG_PAGE_RANGE);
+                        }
+                    }
 
                     PrintAttributes attributes = printJob.getAttributes();
                     if (attributes != null) {
                         serializer.startTag(null, TAG_ATTRIBUTES);
 
-                            //TODO: Implement persistence of the attributes below.
-
-//                            MediaSize mediaSize = attributes.getMediaSize();
-//                            if (mediaSize != null) {
-//                                serializer.attribute(null, ATTRIBUTE_MEDIA_SIZE,
-//                                        mediaSize.flattenToString());
-//                            }
-//
-//                            Resolution resolution = attributes.getResolution();
-//                            if (resolution != null) {
-//                                serializer.attribute(null, ATTRIBUTE_RESOLUTION,
-//                                        resolution.flattenToString());
-//                            }
-//
-//                            Margins margins = attributes.getMargins();
-//                            if (margins != null) {
-//                                serializer.attribute(null, ATTRIBUTE_MARGINS,
-//                                        margins.flattenToString());
-//                            }
-//
-//                            Tray inputTray = attributes.getInputTray();
-//                            if (inputTray != null) {
-//                                serializer.attribute(null, ATTRIBUTE_INPUT_TRAY,
-//                                        inputTray.flattenToString());
-//                            }
-//
-//                            Tray outputTray = attributes.getOutputTray();
-//                            if (outputTray != null) {
-//                                serializer.attribute(null, ATTRIBUTE_OUTPUT_TRAY,
-//                                        outputTray.flattenToString());
-//                            }
-
                         final int duplexMode = attributes.getDuplexMode();
-                        if (duplexMode > 0) {
-                            serializer.attribute(null, ATTRIBUTE_DUPLEX_MODE,
-                                    String.valueOf(duplexMode));
-                        }
+                        serializer.attribute(null, ATTR_DUPLEX_MODE,
+                                String.valueOf(duplexMode));
 
                         final int colorMode = attributes.getColorMode();
-                        if (colorMode > 0) {
-                            serializer.attribute(null, ATTRIBUTE_COLOR_MODE,
-                                    String.valueOf(colorMode));
-                        }
+                        serializer.attribute(null, ATTR_COLOR_MODE,
+                                String.valueOf(colorMode));
 
                         final int fittingMode = attributes.getFittingMode();
-                        if (fittingMode > 0) {
-                            serializer.attribute(null, ATTRIBUTE_FITTING_MODE,
-                                    String.valueOf(fittingMode));
-                        }
+                        serializer.attribute(null, ATTR_FITTING_MODE,
+                                String.valueOf(fittingMode));
 
                         final int orientation = attributes.getOrientation();
-                        if (orientation > 0) {
-                            serializer.attribute(null, ATTRIBUTE_ORIENTATION,
-                                    String.valueOf(orientation));
+                        serializer.attribute(null, ATTR_ORIENTATION,
+                                String.valueOf(orientation));
+
+                        MediaSize mediaSize = attributes.getMediaSize();
+                        if (mediaSize != null) {
+                            serializer.startTag(null, TAG_MEDIA_SIZE);
+                            serializer.attribute(null, ATTR_ID, mediaSize.getId());
+                            serializer.attribute(null, ATTR_LABEL, mediaSize.getLabel()
+                                    .toString());
+                            serializer.attribute(null, ATTR_WIDTH_MILS, String.valueOf(
+                                    mediaSize.getWidthMils()));
+                            serializer.attribute(null, ATTR_HEIGHT_MILS,String.valueOf(
+                                    mediaSize.getHeightMils()));
+                            serializer.endTag(null, TAG_MEDIA_SIZE);
+                        }
+
+                        Resolution resolution = attributes.getResolution();
+                        if (resolution != null) {
+                            serializer.startTag(null, TAG_RESOLUTION);
+                            serializer.attribute(null, ATTR_ID, resolution.getId());
+                            serializer.attribute(null, ATTR_LABEL, resolution.getLabel()
+                                    .toString());
+                            serializer.attribute(null, ATTR_HORIZONTAL_DPI, String.valueOf(
+                                     resolution.getHorizontalDpi()));
+                            serializer.attribute(null, ATTR_VERTICAL_DPI, String.valueOf(
+                                    resolution.getVerticalDpi()));
+                            serializer.endTag(null, TAG_RESOLUTION);
+                        }
+
+                        Margins margins = attributes.getMargins();
+                        if (margins != null) {
+                            serializer.startTag(null, TAG_MARGINS);
+                            serializer.attribute(null, ATTR_LEFT_MILS, String.valueOf(
+                                    margins.getLeftMils()));
+                            serializer.attribute(null, ATTR_TOP_MILS, String.valueOf(
+                                    margins.getTopMils()));
+                            serializer.attribute(null, ATTR_RIGHT_MILS, String.valueOf(
+                                    margins.getRightMils()));
+                            serializer.attribute(null, ATTR_BOTTOM_MILS, String.valueOf(
+                                    margins.getBottomMils()));
+                            serializer.endTag(null, TAG_MARGINS);
+                        }
+
+                        Tray inputTray = attributes.getInputTray();
+                        if (inputTray != null) {
+                            serializer.startTag(null, TAG_INPUT_TRAY);
+                            serializer.attribute(null, ATTR_ID, inputTray.getId());
+                            serializer.attribute(null, ATTR_LABEL, inputTray.getLabel()
+                                    .toString());
+                            serializer.endTag(null, TAG_INPUT_TRAY);
+                        }
+
+                        Tray outputTray = attributes.getOutputTray();
+                        if (outputTray != null) {
+                            serializer.startTag(null, TAG_OUTPUT_TRAY);
+                            serializer.attribute(null, ATTR_ID, outputTray.getId());
+                            serializer.attribute(null, ATTR_LABEL, outputTray.getLabel()
+                                    .toString());
+                            serializer.endTag(null, TAG_OUTPUT_TRAY);
                         }
 
                         serializer.endTag(null, TAG_ATTRIBUTES);
                     }
 
+                    PrintDocumentInfo documentInfo = printJob.getDocumentInfo();
+                    if (documentInfo != null) {
+                        serializer.startTag(null, TAG_DOCUMENT_INFO);
+                        serializer.attribute(null, ATTR_CONTENT_TYPE, String.valueOf(
+                                documentInfo.getContentType()));
+                        serializer.attribute(null, ATTR_PAGE_COUNT, String.valueOf(
+                                documentInfo.getPageCount()));
+                        serializer.endTag(null, TAG_DOCUMENT_INFO);
+                    }
+
                     serializer.endTag(null, TAG_JOB);
 
                     if (DEBUG_PERSISTENCE) {
@@ -567,125 +846,169 @@
             if (!accept(parser, XmlPullParser.START_TAG, TAG_JOB)) {
                 return false;
             }
-            parser.next();
-
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.START_TAG, TAG_ID);
-            parser.next();
-            final int printJobId = Integer.parseInt(parser.getText());
-            parser.next();
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.END_TAG, TAG_ID);
-            parser.next();
-
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.START_TAG, TAG_TAG);
-            parser.next();
-            String tag = parser.getText();
-            parser.next();
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.END_TAG, TAG_TAG);
-            parser.next();
-
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.START_TAG, TAG_APP_ID);
-            parser.next();
-            final int appId = Integer.parseInt(parser.getText());
-            parser.next();
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.END_TAG, TAG_APP_ID);
-            parser.next();
-
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.START_TAG, TAG_LABEL);
-            parser.next();
-            String label = parser.getText();
-            parser.next();
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.END_TAG, TAG_LABEL);
-            parser.next();
-
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.START_TAG, TAG_STATE);
-            parser.next();
-            final int state = Integer.parseInt(parser.getText());
-            parser.next();
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.END_TAG, TAG_STATE);
-            parser.next();
-
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.START_TAG, TAG_PRINTER);
-            parser.next();
-            PrinterId printerId = PrinterId.unflattenFromString(parser.getText());
-            parser.next();
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.END_TAG, TAG_PRINTER);
-            parser.next();
-
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES);
-
-            final int attributeCount = parser.getAttributeCount();
-            PrintAttributes attributes = null;
-            if (attributeCount > 0) {
-                PrintAttributes.Builder builder = new PrintAttributes.Builder();
-
-                // TODO: Implement reading of the attributes below.
-
-//                String mediaSize = parser.getAttributeValue(null, ATTRIBUTE_MEDIA_SIZE);
-//                if (mediaSize != null) {
-//                    builder.setMediaSize(MediaSize.unflattenFromString(mediaSize));
-//                }
-//
-//                String resolution = parser.getAttributeValue(null, ATTRIBUTE_RESOLUTION);
-//                if (resolution != null) {
-//                    builder.setMediaSize(Resolution.unflattenFromString(resolution));
-//                }
-//
-//                String margins = parser.getAttributeValue(null, ATTRIBUTE_MARGINS);
-//                if (margins != null) {
-//                    builder.setMediaSize(Margins.unflattenFromString(margins));
-//                }
-//
-//                String inputTray = parser.getAttributeValue(null, ATTRIBUTE_INPUT_TRAY);
-//                if (inputTray != null) {
-//                    builder.setMediaSize(Tray.unflattenFromString(inputTray));
-//                }
-//
-//                String outputTray = parser.getAttributeValue(null, ATTRIBUTE_OUTPUT_TRAY);
-//                if (outputTray != null) {
-//                    builder.setMediaSize(Tray.unflattenFromString(outputTray));
-//                }
-//
-//                String duplexMode = parser.getAttributeValue(null, ATTRIBUTE_DUPLEX_MODE);
-//                if (duplexMode != null) {
-//                    builder.setDuplexMode(Integer.parseInt(duplexMode));
-//                }
-
-                String colorMode = parser.getAttributeValue(null, ATTRIBUTE_COLOR_MODE);
-                if (colorMode != null) {
-                    builder.setColorMode(Integer.parseInt(colorMode));
-                }
-
-                String fittingMode = parser.getAttributeValue(null, ATTRIBUTE_COLOR_MODE);
-                if (fittingMode != null) {
-                    builder.setFittingMode(Integer.parseInt(fittingMode));
-                }
-            }
-            parser.next();
-            skipEmptyTextTags(parser);
-            expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
-            parser.next();
 
             PrintJobInfo printJob = new PrintJobInfo();
+
+            final int printJobId = Integer.parseInt(parser.getAttributeValue(null, ATTR_ID));
             printJob.setId(printJobId);
-            printJob.setTag(tag);
-            printJob.setAppId(appId);
+            String label = parser.getAttributeValue(null, ATTR_LABEL);
             printJob.setLabel(label);
+            final int state = Integer.parseInt(parser.getAttributeValue(null, ATTR_STATE));
             printJob.setState(state);
-            printJob.setAttributes(attributes);
-            printJob.setPrinterId(printerId);
+            final int appId = Integer.parseInt(parser.getAttributeValue(null, ATTR_APP_ID));
+            printJob.setAppId(appId);
+            final int userId = Integer.parseInt(parser.getAttributeValue(null, ATTR_USER_ID));
+            printJob.setUserId(userId);
+            String tag = parser.getAttributeValue(null, ATTR_TAG);
+            printJob.setTag(tag);
+
+            parser.next();
+
+            skipEmptyTextTags(parser);
+            if (accept(parser, XmlPullParser.START_TAG, TAG_PRINTER_ID)) {
+                String localId = parser.getAttributeValue(null, ATTR_LOCAL_ID);
+                ComponentName service = ComponentName.unflattenFromString(parser.getAttributeValue(
+                        null, ATTR_SERVICE));
+                printJob.setPrinterId(new PrinterId(service, localId));
+                parser.next();
+                skipEmptyTextTags(parser);
+                expect(parser, XmlPullParser.END_TAG, TAG_PRINTER_ID);
+                parser.next();
+            }
+
+            skipEmptyTextTags(parser);
+            List<PageRange> pageRanges = null;
+            while (accept(parser, XmlPullParser.START_TAG, TAG_PAGE_RANGE)) {
+                final int start = Integer.parseInt(parser.getAttributeValue(null, ATTR_START));
+                final int end = Integer.parseInt(parser.getAttributeValue(null, ATTR_END));
+                PageRange pageRange = new PageRange(start, end);
+                if (pageRanges == null) {
+                    pageRanges = new ArrayList<PageRange>();
+                }
+                pageRanges.add(pageRange);
+                parser.next();
+                skipEmptyTextTags(parser);
+                expect(parser, XmlPullParser.END_TAG, TAG_PAGE_RANGE);
+                parser.next();
+            }
+            if (pageRanges != null) {
+                printJob.setPages((PageRange[]) pageRanges.toArray());
+            }
+
+            skipEmptyTextTags(parser);
+            if (accept(parser, XmlPullParser.START_TAG, TAG_ATTRIBUTES)) {
+
+                PrintAttributes.Builder builder = new PrintAttributes.Builder();
+
+                String duplexMode = parser.getAttributeValue(null, ATTR_DUPLEX_MODE);
+                builder.setDuplexMode(Integer.parseInt(duplexMode));
+
+                String colorMode = parser.getAttributeValue(null, ATTR_COLOR_MODE);
+                builder.setColorMode(Integer.parseInt(colorMode));
+
+                String fittingMode = parser.getAttributeValue(null, ATTR_FITTING_MODE);
+                builder.setFittingMode(Integer.parseInt(fittingMode));
+
+                String orientation = parser.getAttributeValue(null, ATTR_ORIENTATION);
+                builder.setOrientation(Integer.parseInt(orientation));
+
+                parser.next();
+
+                skipEmptyTextTags(parser);
+                if (accept(parser, XmlPullParser.START_TAG, TAG_MEDIA_SIZE)) {
+                    String id = parser.getAttributeValue(null, ATTR_ID);
+                    label = parser.getAttributeValue(null, ATTR_LABEL);
+                    final int widthMils = Integer.parseInt(parser.getAttributeValue(null,
+                            ATTR_WIDTH_MILS));
+                    final int heightMils = Integer.parseInt(parser.getAttributeValue(null,
+                            ATTR_HEIGHT_MILS));
+                    MediaSize mediaSize = new MediaSize(id, label, widthMils, heightMils);
+                    builder.setMediaSize(mediaSize);
+                    parser.next();
+                    skipEmptyTextTags(parser);
+                    expect(parser, XmlPullParser.END_TAG, TAG_MEDIA_SIZE);
+                    parser.next();
+                }
+
+                skipEmptyTextTags(parser);
+                if (accept(parser, XmlPullParser.START_TAG, TAG_RESOLUTION)) {
+                    String id = parser.getAttributeValue(null, ATTR_ID);
+                    label = parser.getAttributeValue(null, ATTR_LABEL);
+                    final int horizontalDpi = Integer.parseInt(parser.getAttributeValue(null,
+                            ATTR_HORIZONTAL_DPI));
+                    final int verticalDpi = Integer.parseInt(parser.getAttributeValue(null,
+                            ATTR_VERTICAL_DPI));
+                    Resolution resolution = new Resolution(id, label, horizontalDpi, verticalDpi);
+                    builder.setResolution(resolution);
+                    parser.next();
+                    skipEmptyTextTags(parser);
+                    expect(parser, XmlPullParser.END_TAG, TAG_RESOLUTION);
+                    parser.next();
+                }
+
+                skipEmptyTextTags(parser);
+                if (accept(parser, XmlPullParser.START_TAG, TAG_MARGINS)) {
+                    final int leftMils = Integer.parseInt(parser.getAttributeValue(null,
+                            ATTR_LEFT_MILS));
+                    final int topMils = Integer.parseInt(parser.getAttributeValue(null,
+                            ATTR_TOP_MILS));
+                    final int rightMils = Integer.parseInt(parser.getAttributeValue(null,
+                            ATTR_RIGHT_MILS));
+                    final int bottomMils = Integer.parseInt(parser.getAttributeValue(null,
+                            ATTR_BOTTOM_MILS));
+                    Margins margins = new Margins(leftMils, topMils, rightMils, bottomMils);
+                    builder.setMargins(margins);
+                    parser.next();
+                    skipEmptyTextTags(parser);
+                    expect(parser, XmlPullParser.END_TAG, TAG_MARGINS);
+                    parser.next();
+                }
+
+                skipEmptyTextTags(parser);
+                if (accept(parser, XmlPullParser.START_TAG, TAG_INPUT_TRAY)) {
+                    String id = parser.getAttributeValue(null, ATTR_ID);
+                    label = parser.getAttributeValue(null, ATTR_LABEL);
+                    Tray tray = new Tray(id, label);
+                    builder.setInputTray(tray);
+                    parser.next();
+                    skipEmptyTextTags(parser);
+                    expect(parser, XmlPullParser.END_TAG, TAG_INPUT_TRAY);
+                    parser.next();
+                }
+
+                skipEmptyTextTags(parser);
+                if (accept(parser, XmlPullParser.START_TAG, TAG_OUTPUT_TRAY)) {
+                    String id = parser.getAttributeValue(null, ATTR_ID);
+                    label = parser.getAttributeValue(null, ATTR_LABEL);
+                    Tray tray = new Tray(id, label);
+                    builder.setOutputTray(tray);
+                    parser.next();
+                    skipEmptyTextTags(parser);
+                    expect(parser, XmlPullParser.END_TAG, TAG_OUTPUT_TRAY);
+                    parser.next();
+                }
+
+                printJob.setAttributes(builder.create());
+
+                skipEmptyTextTags(parser);
+                expect(parser, XmlPullParser.END_TAG, TAG_ATTRIBUTES);
+                parser.next();
+            }
+
+            skipEmptyTextTags(parser);
+            if (accept(parser, XmlPullParser.START_TAG, TAG_DOCUMENT_INFO)) {
+                final int pageCount = Integer.parseInt(parser.getAttributeValue(null,
+                        ATTR_PAGE_COUNT));
+                final int contentType = Integer.parseInt(parser.getAttributeValue(null,
+                        ATTR_CONTENT_TYPE));
+                PrintDocumentInfo info = new PrintDocumentInfo.Builder().setPageCount(pageCount)
+                        .setContentType(contentType).create();
+                printJob.setDocumentInfo(info);
+                parser.next();
+                skipEmptyTextTags(parser);
+                expect(parser, XmlPullParser.END_TAG, TAG_DOCUMENT_INFO);
+                parser.next();
+            }
 
             mPrintJobs.add(printJob);
 
diff --git a/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java b/packages/PrintSpooler/src/com/android/printspooler/PrintSpoolerService.java
index 57c4557..26d2a33 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,32 +65,34 @@
 
     @Override
     public IBinder onBind(Intent intent) {
-        return new IPrintSpoolerService.Stub() {
+        mSpooler.restorePersistedState();
+
+        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);
+                    printJobs = mSpooler.getPrintJobInfos(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 {
-                    printJob = mSpooler.getPrintJob(printJobId, appId);
+                    printJob = mSpooler.getPrintJobInfo(printJobId, appId);
                 } finally {
                     callback.onGetPrintJobInfoResult(printJob, sequence);
                 }
             }
 
             @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 +105,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 +137,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 +151,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 +165,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..2bf62ee
--- /dev/null
+++ b/packages/PrintSpooler/src/com/android/printspooler/RemotePrintDocumentAdapter.java
@@ -0,0 +1,444 @@
+/*
+ * 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.Bundle;
+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 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;
+
+import libcore.io.IoUtils;
+
+/**
+ * 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;
+    public static final int STATE_FAILED = 6;
+
+    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
+                    && mState != STATE_FINISH_COMPLETED) {
+                throw new IllegalStateException("Write not completed");
+            }
+            return mFile;
+        }
+    }
+
+    public void start() {
+        QueuedAsyncTask task = new QueuedAsyncTask() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "start()");
+                }
+                synchronized (mLock) {
+                    mTaskQueue.add(this);
+                    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;
+            }
+        };
+        task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+    }
+
+    public void layout(PrintAttributes oldAttributes, PrintAttributes newAttributes,
+            LayoutResultCallback callback, Bundle metadata) {
+        LayoutAsyncTask task = new LayoutAsyncTask(oldAttributes, newAttributes, callback,
+                metadata);
+        task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+    }
+
+    public void write(List<PageRange> pages, WriteResultCallback callback) {
+        WriteAsyncTask task = new WriteAsyncTask(pages, callback);
+        task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, (Void[]) null);
+    }
+
+    public void finish(final boolean abortPendingWork) {
+        QueuedAsyncTask task = new QueuedAsyncTask() {
+            @Override
+            protected Void doInBackground(Void... params) {
+                if (DEBUG) {
+                    Log.i(LOG_TAG, "finish");
+                }
+                synchronized (mLock) {
+                    if (abortPendingWork) {
+                        final int taskCount = mTaskQueue.size();
+                        for (int i = taskCount - 1; i >= 0; i--) {
+                            mTaskQueue.remove(i).cancel();
+                        }
+                    }
+                    mTaskQueue.add(this);
+                    if (mState < STATE_START_COMPLETED) {
+                        return null;
+                    }
+                }
+                try {
+                    mRemoteInterface.finish();
+                    synchronized (mLock) {
+                        mState = STATE_FINISH_COMPLETED;
+                    }
+                } catch (RemoteException re) {
+                    Log.e(LOG_TAG, "Error reading file", re);
+                    mState = STATE_FAILED;
+                }
+                return null;
+            }
+        };
+        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 Bundle mMetadata;
+
+        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, Bundle metadata) {
+            mOldAttributes = oldAttributes;
+            mNewAttributes = newAttributes;
+            mCallback = callback;
+            mMetadata = metadata;
+        }
+
+        @Override
+        public void cancel() {
+            synchronized (mLock) {
+                throwIfCancelledLocked();
+                cancelSignalQuietlyLocked();
+            }
+            super.cancel();
+        }
+
+        @Override
+        protected Void doInBackground(Void... params) {
+            synchronized (mLock) {
+                mTaskQueue.add(this);
+                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, mMetadata);
+                synchronized (mLock) {
+                    while (true) {
+                        if (isCancelled()) {
+                            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_FAILED;
+            }
+            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) {
+                mTaskQueue.add(this);
+                if (mState != STATE_LAYOUT_COMPLETED
+                        && mState != STATE_WRITE_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()) {
+                            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_FAILED;
+            } catch (IOException ioe) {
+                Slog.e(LOG_TAG, "Error writing print document", ioe);
+                mState = STATE_FAILED;
+            } 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/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index dfc68f4..2629b11 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -64,6 +64,9 @@
     <uses-permission android:name="android.permission.READ_DREAM_STATE" />
     <uses-permission android:name="android.permission.WRITE_DREAM_STATE" />
 
+    <!-- Alarm clocks -->
+    <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
+
     <application
         android:persistent="true"
         android:allowClearUserData="false"
diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml
index 5e18d0a..190c4be 100644
--- a/packages/SystemUI/res/values-nl/strings.xml
+++ b/packages/SystemUI/res/values-nl/strings.xml
@@ -63,13 +63,13 @@
     <string name="compat_mode_off" msgid="4434467572461327898">"Rek uit v. schermvulling"</string>
     <string name="compat_mode_help_header" msgid="7969493989397529910">"Compatibiliteitszoom"</string>
     <string name="compat_mode_help_body" msgid="4946726776359270040">"Wanneer een app is ontworpen voor een kleiner scherm, wordt naast de klok een zoomknop weergegeven."</string>
-    <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Schermafbeelding opslaan..."</string>
-    <string name="screenshot_saving_title" msgid="8242282144535555697">"Schermafbeelding opslaan..."</string>
-    <string name="screenshot_saving_text" msgid="2419718443411738818">"Schermafbeelding wordt opgeslagen."</string>
-    <string name="screenshot_saved_title" msgid="6461865960961414961">"Schermafbeelding gemaakt."</string>
-    <string name="screenshot_saved_text" msgid="1152839647677558815">"Raak aan om uw schermafbeelding te bekijken."</string>
-    <string name="screenshot_failed_title" msgid="705781116746922771">"Schermafbeelding is niet gemaakt."</string>
-    <string name="screenshot_failed_text" msgid="8134011269572415402">"Kan schermafbeelding niet opslaan. Mogelijk is de opslag in gebruik."</string>
+    <string name="screenshot_saving_ticker" msgid="7403652894056693515">"Screenshot opslaan..."</string>
+    <string name="screenshot_saving_title" msgid="8242282144535555697">"Screenshot opslaan..."</string>
+    <string name="screenshot_saving_text" msgid="2419718443411738818">"Screenshot wordt opgeslagen."</string>
+    <string name="screenshot_saved_title" msgid="6461865960961414961">"Screenshot gemaakt."</string>
+    <string name="screenshot_saved_text" msgid="1152839647677558815">"Raak aan om uw screenshot te bekijken."</string>
+    <string name="screenshot_failed_title" msgid="705781116746922771">"Screenshot is niet gemaakt."</string>
+    <string name="screenshot_failed_text" msgid="8134011269572415402">"Kan screenshot niet opslaan. Mogelijk is de opslag in gebruik."</string>
     <string name="usb_preference_title" msgid="6551050377388882787">"Opties voor USB-bestandsoverdracht"</string>
     <string name="use_mtp_button_title" msgid="4333504413563023626">"Koppelen als mediaspeler (MTP)"</string>
     <string name="use_ptp_button_title" msgid="7517127540301625751">"Koppelen als camera (PTP)"</string>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index fa1e3fe..4fd8aee 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -109,7 +109,7 @@
 
     private View mScrollView;
 
-    private OnScaleGestureListener mScaleGestureListener 
+    private OnScaleGestureListener mScaleGestureListener
             = new ScaleGestureDetector.SimpleOnScaleGestureListener() {
         @Override
         public boolean onScaleBegin(ScaleGestureDetector detector) {
diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
index ca6a06f..6fa863d 100644
--- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java
@@ -140,7 +140,7 @@
                 "\nvoid main(void) {\n" +
                 "    gl_FragColor = texture2D(texture, outTexCoords);\n" +
                 "}\n\n";
-    
+
         private static final int FLOAT_SIZE_BYTES = 4;
         private static final int TRIANGLE_VERTICES_DATA_STRIDE_BYTES = 5 * FLOAT_SIZE_BYTES;
         private static final int TRIANGLE_VERTICES_DATA_POS_OFFSET = 0;
@@ -184,7 +184,7 @@
             }
 
             super.onCreate(surfaceHolder);
-            
+
             // TODO: Don't need this currently because the wallpaper service
             // will restart the image wallpaper whenever the image changes.
             //IntentFilter filter = new IntentFilter(Intent.ACTION_WALLPAPER_CHANGED);
@@ -512,44 +512,44 @@
 
         private int loadTexture(Bitmap bitmap) {
             int[] textures = new int[1];
-    
+
             glActiveTexture(GL_TEXTURE0);
             glGenTextures(1, textures, 0);
             checkGlError();
-    
+
             int texture = textures[0];
             glBindTexture(GL_TEXTURE_2D, texture);
             checkGlError();
-            
+
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-    
+
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
             glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
-    
+
             GLUtils.texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bitmap, GL_UNSIGNED_BYTE, 0);
             checkGlError();
 
             return texture;
         }
-        
+
         private int buildProgram(String vertex, String fragment) {
             int vertexShader = buildShader(vertex, GL_VERTEX_SHADER);
             if (vertexShader == 0) return 0;
-    
+
             int fragmentShader = buildShader(fragment, GL_FRAGMENT_SHADER);
             if (fragmentShader == 0) return 0;
-    
+
             int program = glCreateProgram();
             glAttachShader(program, vertexShader);
             checkGlError();
-    
+
             glAttachShader(program, fragmentShader);
             checkGlError();
-    
+
             glLinkProgram(program);
             checkGlError();
-    
+
             int[] status = new int[1];
             glGetProgramiv(program, GL_LINK_STATUS, status, 0);
             if (status[0] != GL_TRUE) {
@@ -560,19 +560,19 @@
                 glDeleteProgram(program);
                 return 0;
             }
-    
+
             return program;
         }
 
         private int buildShader(String source, int type) {
             int shader = glCreateShader(type);
-    
+
             glShaderSource(shader, source);
             checkGlError();
-    
+
             glCompileShader(shader);
             checkGlError();
-    
+
             int[] status = new int[1];
             glGetShaderiv(shader, GL_COMPILE_STATUS, status, 0);
             if (status[0] != GL_TRUE) {
@@ -581,7 +581,7 @@
                 glDeleteShader(shader);
                 return 0;
             }
-            
+
             return shader;
         }
 
@@ -608,24 +608,24 @@
 
         private boolean initGL(SurfaceHolder surfaceHolder) {
             mEgl = (EGL10) EGLContext.getEGL();
-    
+
             mEglDisplay = mEgl.eglGetDisplay(EGL_DEFAULT_DISPLAY);
             if (mEglDisplay == EGL_NO_DISPLAY) {
                 throw new RuntimeException("eglGetDisplay failed " +
                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
             }
-            
+
             int[] version = new int[2];
             if (!mEgl.eglInitialize(mEglDisplay, version)) {
                 throw new RuntimeException("eglInitialize failed " +
                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
             }
-    
+
             mEglConfig = chooseEglConfig();
             if (mEglConfig == null) {
                 throw new RuntimeException("eglConfig not initialized");
             }
-            
+
             mEglContext = createContext(mEgl, mEglDisplay, mEglConfig);
             if (mEglContext == EGL_NO_CONTEXT) {
                 throw new RuntimeException("createContext failed " +
@@ -667,7 +667,7 @@
                 throw new RuntimeException("createWindowSurface failed " +
                         GLUtils.getEGLErrorString(error));
             }
-    
+
             if (!mEgl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
                 throw new RuntimeException("eglMakeCurrent failed " +
                         GLUtils.getEGLErrorString(mEgl.eglGetError()));
@@ -675,13 +675,13 @@
 
             return true;
         }
-        
-    
+
+
         EGLContext createContext(EGL10 egl, EGLDisplay eglDisplay, EGLConfig eglConfig) {
             int[] attrib_list = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
-            return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);            
+            return egl.eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, attrib_list);
         }
-    
+
         private EGLConfig chooseEglConfig() {
             int[] configsCount = new int[1];
             EGLConfig[] configs = new EGLConfig[1];
diff --git a/packages/SystemUI/src/com/android/systemui/LoadAverageService.java b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
index 37e974b..efbee5b 100644
--- a/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
+++ b/packages/SystemUI/src/com/android/systemui/LoadAverageService.java
@@ -33,18 +33,18 @@
 
 public class LoadAverageService extends Service {
     private View mView;
-    
+
     private static final class Stats extends ProcessStats {
         String mLoadText;
         int mLoadWidth;
-        
+
         private final Paint mPaint;
-        
+
         Stats(Paint paint) {
             super(false);
             mPaint = paint;
         }
-        
+
         @Override
         public void onLoadChanged(float load1, float load5, float load15) {
             mLoadText = load1 + " / " + load5 + " / " + load15;
@@ -56,7 +56,7 @@
             return (int)mPaint.measureText(name);
         }
     }
-    
+
     private class LoadView extends View {
         private Handler mHandler = new Handler() {
             @Override
@@ -71,7 +71,7 @@
         };
 
         private final Stats mStats;
-        
+
         private Paint mLoadPaint;
         private Paint mAddedPaint;
         private Paint mRemovedPaint;
@@ -186,7 +186,7 @@
             final int irqTime = stats.getLastIrqTime();
             final int softIrqTime = stats.getLastSoftIrqTime();
             final int idleTime = stats.getLastIdleTime();
-            
+
             final int totalTime = userTime+systemTime+iowaitTime+irqTime+softIrqTime+idleTime;
             if (totalTime == 0) {
                 return;
@@ -269,7 +269,7 @@
                     maxWidth = st.nameWidth;
                 }
             }
-            
+
             int neededWidth = mPaddingLeft + mPaddingRight + maxWidth;
             int neededHeight = mPaddingTop + mPaddingBottom + (mFH*(1+NW));
             if (neededWidth != mNeededWidth || neededHeight != mNeededHeight) {
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUI.java b/packages/SystemUI/src/com/android/systemui/SystemUI.java
index fec7329..cb624ad 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUI.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUI.java
@@ -28,7 +28,7 @@
     public Map<Class<?>, Object> mComponents;
 
     public abstract void start();
-    
+
     protected void onConfigurationChanged(Configuration newConfig) {
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index e084dac..a08eb9b 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -283,7 +283,7 @@
         d.show();
         mInvalidChargerDialog = d;
     }
-    
+
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.print("mLowBatteryAlertCloseLevel=");
         pw.println(mLowBatteryAlertCloseLevel);
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
index 5617520..119299f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java
@@ -222,12 +222,12 @@
             sharingIntent.putExtra(Intent.EXTRA_SUBJECT, subject);
 
             Intent chooserIntent = Intent.createChooser(sharingIntent, null);
-            chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK 
+            chooserIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
                     | Intent.FLAG_ACTIVITY_NEW_TASK);
 
             mNotificationBuilder.addAction(R.drawable.ic_menu_share,
                      r.getString(com.android.internal.R.string.share),
-                     PendingIntent.getActivity(context, 0, chooserIntent, 
+                     PendingIntent.getActivity(context, 0, chooserIntent,
                              PendingIntent.FLAG_CANCEL_CURRENT));
 
             OutputStream out = resolver.openOutputStream(uri);
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/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 8b50a38..5bda813 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -166,7 +166,7 @@
     /**
      * Returns the right icon to use for this item, respecting the iconId and
      * iconPackage (if set)
-     * 
+     *
      * @param context Context to use to get resources if iconPackage is not set
      * @return Drawable for this item, or null if the package or item could not
      *         be found
@@ -193,7 +193,7 @@
         if (icon.iconId == 0) {
             return null;
         }
-        
+
         try {
             return r.getDrawable(icon.iconId);
         } catch (RuntimeException e) {
@@ -285,7 +285,7 @@
     }
 
     public String toString() {
-        return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon 
+        return "StatusBarIconView(slot=" + mSlot + " icon=" + mIcon
             + " notification=" + mNotification + ")";
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 9eaee30..de33b87 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -370,11 +370,11 @@
 
     @Override
     public void onFinishInflate() {
-        mRotatedViews[Surface.ROTATION_0] = 
+        mRotatedViews[Surface.ROTATION_0] =
         mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
 
         mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
-        
+
         mRotatedViews[Surface.ROTATION_270] = NAVBAR_ALWAYS_AT_RIGHT
                                                 ? findViewById(R.id.rot90)
                                                 : findViewById(R.id.rot270);
@@ -434,7 +434,7 @@
     @Override
     protected void onLayout (boolean changed, int left, int top, int right, int bottom) {
         if (DEBUG) Log.d(TAG, String.format(
-                    "onLayout: %s (%d,%d,%d,%d)", 
+                    "onLayout: %s (%d,%d,%d,%d)",
                     changed?"changed":"notchanged", left, top, right, bottom));
         super.onLayout(changed, left, top, right, bottom);
     }
@@ -450,7 +450,7 @@
         return super.onInterceptTouchEvent(ev);
     }
     */
-        
+
 
     private String getResourceName(int resId) {
         if (resId != 0) {
@@ -491,7 +491,7 @@
 
         getWindowVisibleDisplayFrame(r);
         final boolean offscreen = r.right > size.x || r.bottom > size.y;
-        pw.println("      window: " 
+        pw.println("      window: "
                 + r.toShortString()
                 + " " + visibilityToString(getWindowVisibility())
                 + (offscreen ? " OFFSCREEN!" : ""));
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
index 751d944..d55754b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
@@ -198,7 +198,7 @@
         }
         if (DEBUG) LOG("collapseAllPanels: animate=%s waiting=%s", animate, waiting);
         if (!waiting && mState != STATE_CLOSED) {
-            // it's possible that nothing animated, so we replicate the termination 
+            // it's possible that nothing animated, so we replicate the termination
             // conditions of panelExpansionChanged here
             go(STATE_CLOSED);
             onAllPanelsCollapsed();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index cf7f9c6..3a17c9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -226,7 +226,7 @@
             return;
         }
         if (mPeekAnimator == null) {
-            mPeekAnimator = ObjectAnimator.ofFloat(this, 
+            mPeekAnimator = ObjectAnimator.ofFloat(this,
                     "expandedHeight", mPeekHeight)
                 .setDuration(250);
         }
@@ -339,7 +339,7 @@
 
         mFlingGestureMaxOutputVelocityPx = res.getDimension(R.dimen.fling_gesture_max_output_velocity);
 
-        mPeekHeight = res.getDimension(R.dimen.peek_height) 
+        mPeekHeight = res.getDimension(R.dimen.peek_height)
             + getPaddingBottom() // our window might have a dropshadow
             - (mHandleView == null ? 0 : mHandleView.getPaddingTop()); // the handle might have a topshadow
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index fd99f5b..6eadd2b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -147,7 +147,7 @@
     private float mExpandAccelPx; // classic value: 2000px/s/s
     private float mCollapseAccelPx; // classic value: 2000px/s/s (will be negated to collapse "up")
 
-    private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little 
+    private float mFlingGestureMaxOutputVelocityPx; // how fast can it really go? (should be a little
                                                     // faster than mSelfCollapseVelocityPx)
 
     PhoneStatusBarPolicy mIconPolicy;
@@ -173,11 +173,11 @@
 
     // viewgroup containing the normal contents of the statusbar
     LinearLayout mStatusBarContents;
-    
+
     // right-hand icons
     LinearLayout mSystemIconArea;
-    
-    // left-hand icons 
+
+    // left-hand icons
     LinearLayout mStatusIcons;
     // the icons themselves
     IconMerger mNotificationIcons;
@@ -204,7 +204,7 @@
 
     // top bar
     View mNotificationPanelHeader;
-    View mDateTimeView; 
+    View mDateTimeView;
     View mClearButton;
     ImageView mSettingsButton, mNotificationButton;
 
@@ -264,7 +264,7 @@
 
     // XXX: gesture research
     private final GestureRecorder mGestureRec = DEBUG_GESTURES
-        ? new GestureRecorder("/sdcard/statusbar_gestures.dat") 
+        ? new GestureRecorder("/sdcard/statusbar_gestures.dat")
         : null;
 
     private int mNavigationIconHints = 0;
@@ -391,7 +391,7 @@
 
         mStatusBarView = (PhoneStatusBarView) mStatusBarWindow.findViewById(R.id.status_bar);
         mStatusBarView.setBar(this);
-        
+
 
         PanelHolder holder = (PanelHolder) mStatusBarWindow.findViewById(R.id.panel_holder);
         mStatusBarView.setPanelHolder(holder);
@@ -629,7 +629,7 @@
             }
         }
 
-        mClingShown = ! (DEBUG_CLINGS 
+        mClingShown = ! (DEBUG_CLINGS
             || !Prefs.read(mContext).getBoolean(Prefs.SHOWN_QUICK_SETTINGS_HELP, false));
 
         if (!ENABLE_NOTIFICATION_PANEL_CLING || ActivityManager.isRunningInTestHarness()) {
@@ -1080,7 +1080,7 @@
 
     protected void updateCarrierLabelVisibility(boolean force) {
         if (!mShowCarrierInPanel) return;
-        // The idea here is to only show the carrier label when there is enough room to see it, 
+        // The idea here is to only show the carrier label when there is enough room to see it,
         // i.e. when there aren't enough notifications to fill the panel.
         if (DEBUG) {
             Log.d(TAG, String.format("pileh=%d scrollh=%d carrierh=%d",
@@ -1092,7 +1092,7 @@
             !(emergencyCallsShownElsewhere && mNetworkController.isEmergencyOnly())
             && mPile.getHeight() < (mNotificationPanel.getHeight() - mCarrierLabelHeight - mNotificationHeaderHeight)
             && mScrollView.getVisibility() == View.VISIBLE;
-        
+
         if (force || mCarrierLabelVisible != makeVisible) {
             mCarrierLabelVisible = makeVisible;
             if (DEBUG) {
@@ -1131,8 +1131,8 @@
                     + " any=" + any + " clearable=" + clearable);
         }
 
-        if (mHasFlipSettings 
-                && mFlipSettingsView != null 
+        if (mHasFlipSettings
+                && mFlipSettingsView != null
                 && mFlipSettingsView.getVisibility() == View.VISIBLE
                 && mScrollView.getVisibility() != View.VISIBLE) {
             // the flip settings panel is unequivocally showing; we should not be shown
@@ -1444,7 +1444,7 @@
         a.setStartDelay(d);
         return a;
     }
-    
+
     public Animator start(Animator a) {
         a.start();
         return a;
@@ -1573,7 +1573,7 @@
                 interpolator(mAccelerateInterpolator,
                         ObjectAnimator.ofFloat(mScrollView, View.SCALE_X, 1f, 0f)
                         )
-                    .setDuration(FLIP_DURATION_OUT), 
+                    .setDuration(FLIP_DURATION_OUT),
                 mScrollView, View.INVISIBLE));
         mSettingsButtonAnim = start(
             setVisibilityWhenDone(
@@ -1674,13 +1674,13 @@
 
     /**
      * Enables or disables layers on the children of the notifications pile.
-     * 
+     *
      * When layers are enabled, this method attempts to enable layers for the minimal
      * number of children. Only children visible when the notification area is fully
      * expanded will receive a layer. The technique used in this method might cause
      * more children than necessary to get a layer (at most one extra child with the
      * current UI.)
-     * 
+     *
      * @param layerType {@link View#LAYER_TYPE_NONE} or {@link View#LAYER_TYPE_HARDWARE}
      */
     private void setPileLayers(int layerType) {
@@ -1693,7 +1693,7 @@
                 }
                 break;
             case View.LAYER_TYPE_HARDWARE:
-                final int[] location = new int[2]; 
+                final int[] location = new int[2];
                 mNotificationPanel.getLocationInWindow(location);
 
                 final int left = location[0];
@@ -1803,9 +1803,9 @@
 
         // Cling (first-run help) handling.
         // The cling is supposed to show the first time you drag, or even tap, the status bar.
-        // It should show the notification panel, then fade in after half a second, giving you 
+        // It should show the notification panel, then fade in after half a second, giving you
         // an explanation of what just happened, as well as teach you how to access quick
-        // settings (another drag). The user can dismiss the cling by clicking OK or by 
+        // settings (another drag). The user can dismiss the cling by clicking OK or by
         // dragging quick settings into view.
         final int act = event.getActionMasked();
         if (mSuppressStatusBarDrags) {
@@ -2284,7 +2284,7 @@
     void updateDisplaySize() {
         mDisplay.getMetrics(mDisplayMetrics);
         if (DEBUG_GESTURES) {
-            mGestureRec.tag("display", 
+            mGestureRec.tag("display",
                     String.format("%dx%d", mDisplayMetrics.widthPixels, mDisplayMetrics.heightPixels));
         }
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index 2a65381..d2e7bd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -114,9 +114,9 @@
 
         if (mFullWidthNotifications) {
             // No double swiping. If either panel is open, nothing else can be pulled down.
-            return ((mSettingsPanel == null ? 0 : mSettingsPanel.getExpandedHeight()) 
-                        + mNotificationPanel.getExpandedHeight() > 0) 
-                    ? null 
+            return ((mSettingsPanel == null ? 0 : mSettingsPanel.getExpandedHeight())
+                        + mNotificationPanel.getExpandedHeight() > 0)
+                    ? null
                     : mNotificationPanel;
         }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
index 9ee6065..a9c5c79 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickSettings.java
@@ -44,6 +44,7 @@
 import android.os.RemoteException;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.provider.AlarmClock;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.provider.ContactsContract.Profile;
@@ -605,12 +606,7 @@
         alarmTile.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                // TODO: Jump into the alarm application
-                Intent intent = new Intent();
-                intent.setComponent(new ComponentName(
-                        "com.google.android.deskclock",
-                        "com.android.deskclock.AlarmClock"));
-                startSettingsActivity(intent);
+                startSettingsActivity(AlarmClock.ACTION_SET_ALARM);
             }
         });
         mModel.addAlarmTile(alarmTile, new QuickSettingsModel.RefreshCallback() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
index a784f37..a6ce288 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/Ticker.java
@@ -38,7 +38,7 @@
 
 public abstract class Ticker {
     private static final int TICKER_SEGMENT_DELAY = 3000;
-    
+
     private Context mContext;
     private Handler mHandler = new Handler();
     private ArrayList<Segment> mSegments = new ArrayList();
@@ -216,15 +216,15 @@
         if (initialCount == 0 && mSegments.size() > 0) {
             Segment seg = mSegments.get(0);
             seg.first = false;
-            
+
             mIconSwitcher.setAnimateFirstView(false);
             mIconSwitcher.reset();
             mIconSwitcher.setImageDrawable(seg.icon);
-            
+
             mTextSwitcher.setAnimateFirstView(false);
             mTextSwitcher.reset();
             mTextSwitcher.setText(seg.getText());
-            
+
             tickerStarting();
             scheduleAdvance();
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 452ff489..575b44e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -71,7 +71,7 @@
 
             boolean plugged = false;
             switch (status) {
-                case BatteryManager.BATTERY_STATUS_CHARGING: 
+                case BatteryManager.BATTERY_STATUS_CHARGING:
                 case BatteryManager.BATTERY_STATUS_FULL:
                     plugged = true;
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index e5816cd..93fb14f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -192,7 +192,7 @@
                 return formatted;
             }
         }
- 
+
         return result;
 
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
index 27a3a15..847f997 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DateView.java
@@ -63,7 +63,7 @@
         mAttachedToWindow = true;
         setUpdates();
     }
-    
+
     @Override
     protected void onDetachedFromWindow() {
         super.onDetachedFromWindow();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
index dbf9957..f6ac4a8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpNotificationView.java
@@ -40,10 +40,10 @@
     Rect mTmpRect = new Rect();
 
     private SwipeHelper mSwipeHelper;
-    
+
     BaseStatusBar mBar;
     private ViewGroup mContentHolder;
-    
+
     private Notification mHeadsUp;
     private OnClickListener mOnClickListener;
 
@@ -62,14 +62,14 @@
         float densityScale = getResources().getDisplayMetrics().density;
         float pagingTouchSlop = ViewConfiguration.get(getContext()).getScaledPagingTouchSlop();
         mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, pagingTouchSlop);
-        
+
         mContentHolder = (ViewGroup) findViewById(R.id.contentHolder);
         if (mHeadsUp != null) {
             // whoops, we're on already!
             applyContent(mHeadsUp, mOnClickListener);
         }
     }
-    
+
     public void setBar(BaseStatusBar bar) {
         mBar = bar;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index fc77be1..f325957 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -81,7 +81,7 @@
                 defStyle, 0);
 
         mCode = a.getInteger(R.styleable.KeyButtonView_keyCode, 0);
-        
+
         mSupportsLongpress = a.getBoolean(R.styleable.KeyButtonView_keyRepeat, true);
 
         mGlowBG = a.getDrawable(R.styleable.KeyButtonView_glowBackground);
@@ -90,7 +90,7 @@
             mGlowWidth = mGlowBG.getIntrinsicWidth();
             mGlowHeight = mGlowBG.getIntrinsicHeight();
         }
-        
+
         a.recycle();
 
         setClickable(true);
@@ -180,7 +180,7 @@
                 }
                 final AnimatorSet as = mPressedAnim = new AnimatorSet();
                 if (pressed) {
-                    if (mGlowScale < GLOW_MAX_SCALE_FACTOR) 
+                    if (mGlowScale < GLOW_MAX_SCALE_FACTOR)
                         mGlowScale = GLOW_MAX_SCALE_FACTOR;
                     if (mGlowAlpha < BUTTON_QUIESCENT_ALPHA)
                         mGlowAlpha = BUTTON_QUIESCENT_ALPHA;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
index 63f1254..c5563da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationController.java
@@ -88,7 +88,7 @@
             textResId = R.string.gps_notification_searching_text;
             visible = true;
         }
-        
+
         try {
             if (visible) {
                 Intent gpsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
@@ -108,14 +108,14 @@
                 // Notification.Builder will helpfully fill these out for you no matter what you do
                 n.tickerView = null;
                 n.tickerText = null;
-                
+
                 n.priority = Notification.PRIORITY_HIGH;
 
                 int[] idOut = new int[1];
                 mNotificationService.enqueueNotificationWithTag(
                         mContext.getPackageName(), mContext.getBasePackageName(),
-                        null, 
-                        GPS_NOTIFICATION_ID, 
+                        null,
+                        GPS_NOTIFICATION_ID,
                         n,
                         idOut,
                         UserHandle.USER_ALL);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
index cd8445c..92c57c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -36,8 +36,8 @@
 
 import java.util.HashMap;
 
-public class NotificationRowLayout 
-        extends LinearLayout 
+public class NotificationRowLayout
+        extends LinearLayout
         implements SwipeHelper.Callback, ExpandHelper.Callback
 {
     private static final String TAG = "NotificationRowLayout";
@@ -55,7 +55,7 @@
     HashMap<View, ValueAnimator> mDisappearingViews = new HashMap<View, ValueAnimator>();
 
     private SwipeHelper mSwipeHelper;
-    
+
     private OnSizeChangedListener mOnSizeChangedListener;
 
     // Flag set during notification removal animation to avoid causing too much work until
@@ -74,7 +74,7 @@
         mRealLayoutTransition = new LayoutTransition();
         mRealLayoutTransition.setAnimateParentHierarchy(true);
         setLayoutTransitionsEnabled(true);
-        
+
         setOrientation(LinearLayout.VERTICAL);
 
         if (DEBUG) {
diff --git a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
index b75f8b3..2c36ab7 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/StorageNotification.java
@@ -41,7 +41,7 @@
 
     /**
      * The notification that is shown when a USB mass storage host
-     * is connected. 
+     * is connected.
      * <p>
      * This is lazily created, so use {@link #setUsbStorageNotification()}.
      */
@@ -217,7 +217,7 @@
             setMediaStorageNotification(
                     com.android.internal.R.string.ext_media_unmountable_notification_title,
                     com.android.internal.R.string.ext_media_unmountable_notification_message,
-                    com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi); 
+                    com.android.internal.R.drawable.stat_notify_sdcard_usb, true, false, pi);
             updateUsbMassStorageNotification(mUmsAvailable);
         } else if (newState.equals(Environment.MEDIA_REMOVED)) {
             /*
@@ -283,7 +283,7 @@
         if (notificationManager == null) {
             return;
         }
-        
+
         if (visible) {
             Resources r = Resources.getSystem();
             CharSequence title = r.getText(titleId);
@@ -300,7 +300,7 @@
             } else {
                 mUsbStorageNotification.defaults &= ~Notification.DEFAULT_SOUND;
             }
-                
+
             mUsbStorageNotification.flags = Notification.FLAG_ONGOING_EVENT;
 
             mUsbStorageNotification.tickerText = title;
@@ -329,7 +329,7 @@
                 mUsbStorageNotification.fullScreenIntent = pi;
             }
         }
-    
+
         final int notificationId = mUsbStorageNotification.icon;
         if (visible) {
             notificationManager.notifyAsUser(null, notificationId, mUsbStorageNotification,
@@ -373,7 +373,7 @@
             final int notificationId = mMediaStorageNotification.icon;
             notificationManager.cancel(notificationId);
         }
-        
+
         if (visible) {
             Resources r = Resources.getSystem();
             CharSequence title = r.getText(titleId);
@@ -402,7 +402,7 @@
             mMediaStorageNotification.icon = icon;
             mMediaStorageNotification.setLatestEventInfo(mContext, title, message, pi);
         }
-    
+
         final int notificationId = mMediaStorageNotification.icon;
         if (visible) {
             notificationManager.notifyAsUser(null, notificationId,
diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
index 65315f3..0ed2a54 100644
--- a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java
@@ -94,7 +94,7 @@
             switchDisplay(on);
         }
     };
-    
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
@@ -105,7 +105,7 @@
                 Log.w(TAG, "Failed to get StorageManager");
             }
         }
-        
+
         mUIHandler = new Handler();
 
         HandlerThread thr = new HandlerThread("SystemUI UsbStorageActivity");
@@ -184,7 +184,7 @@
     @Override
     protected void onPause() {
         super.onPause();
-        
+
         unregisterReceiver(mUsbStateReceiver);
         if (mStorageManager == null && mStorageListener != null) {
             mStorageManager.unregisterListener(mStorageListener);
@@ -256,7 +256,7 @@
                 // will be hidden once USB mass storage kicks in (or fails)
             }
         });
-        
+
         // things to do elsewhere
         mAsyncStorageHandler.post(new Runnable() {
             @Override
diff --git a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
index b72bb2b..417527c 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneFallbackEventHandler.java
@@ -102,7 +102,8 @@
             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
             case KeyEvent.KEYCODE_MEDIA_REWIND:
             case KeyEvent.KEYCODE_MEDIA_RECORD:
-            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+            case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
                 handleMediaKeyEvent(event);
                 return true;
             }
@@ -215,7 +216,8 @@
             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
             case KeyEvent.KEYCODE_MEDIA_REWIND:
             case KeyEvent.KEYCODE_MEDIA_RECORD:
-            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+            case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
                 handleMediaKeyEvent(event);
                 return true;
             }
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 0b8b028..bd56ee0 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -33,6 +33,7 @@
 import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.content.res.Resources;
@@ -1044,10 +1045,8 @@
         // SystemUI (status bar) layout policy
         int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / density;
 
-        if (shortSizeDp < 600) {
-            // Allow the navigation bar to move on small devices (phones).
-            mNavigationBarCanMove = true;
-        }
+        // Allow the navigation bar to move on small devices (phones).
+        mNavigationBarCanMove = shortSizeDp < 600;
 
         mHasNavigationBar = mContext.getResources().getBoolean(
                 com.android.internal.R.bool.config_showNavigationBar);
@@ -1231,6 +1230,7 @@
             case TYPE_DREAM:
             case TYPE_INPUT_METHOD:
             case TYPE_WALLPAPER:
+            case TYPE_PRIVATE_PRESENTATION:
                 // The window manager will check these.
                 break;
             case TYPE_PHONE:
@@ -1293,6 +1293,7 @@
             case TYPE_SYSTEM_DIALOG:
             case TYPE_UNIVERSE_BACKGROUND:
             case TYPE_VOLUME_OVERLAY:
+            case TYPE_PRIVATE_PRESENTATION:
                 break;
         }
 
@@ -1365,6 +1366,8 @@
         switch (type) {
         case TYPE_UNIVERSE_BACKGROUND:
             return 1;
+        case TYPE_PRIVATE_PRESENTATION:
+            return 2;
         case TYPE_WALLPAPER:
             // wallpaper is at the bottom, though the window manager may move it.
             return 2;
@@ -3924,7 +3927,8 @@
             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
             case KeyEvent.KEYCODE_MEDIA_REWIND:
             case KeyEvent.KEYCODE_MEDIA_RECORD:
-            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
+            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+            case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
                 if ((result & ACTION_PASS_TO_USER) == 0) {
                     // Only do this if we would otherwise not pass it to the user. In that
                     // case, the PhoneWindow class will do the same thing, except it will
@@ -3992,6 +3996,7 @@
             case KeyEvent.KEYCODE_MEDIA_REWIND:
             case KeyEvent.KEYCODE_MEDIA_RECORD:
             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+            case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK:
             case KeyEvent.KEYCODE_CAMERA:
                 return false;
         }
@@ -4789,8 +4794,8 @@
         ActivityInfo ai = null;
         ResolveInfo info = mContext.getPackageManager().resolveActivityAsUser(
                 intent,
-                PackageManager.MATCH_DEFAULT_ONLY,
-                UserHandle.USER_CURRENT);
+                PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA,
+                getCurrentUserId());
         if (info != null) {
             ai = info.activityInfo;
         }
@@ -4805,6 +4810,16 @@
         return null;
     }
 
+    private int getCurrentUserId() {
+        try {
+            UserInfo user = ActivityManagerNative.getDefault().getCurrentUser();
+            return user != null ? user.id : UserHandle.USER_NULL;
+        } catch (RemoteException e) {
+            // noop
+        }
+        return UserHandle.USER_NULL;
+    }
+
     void startDockOrHome() {
         awakenDreams();
 
diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp
index 65749b3..fab091f 100644
--- a/services/input/EventHub.cpp
+++ b/services/input/EventHub.cpp
@@ -1188,6 +1188,11 @@
             mBuiltInKeyboardId = device->id;
         }
 
+        // 'Q' key support = cheap test of whether this is an alpha-capable kbd
+        if (hasKeycodeLocked(device, AKEYCODE_Q)) {
+            device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
+        }
+
         // See if this device has a DPAD.
         if (hasKeycodeLocked(device, AKEYCODE_DPAD_UP) &&
                 hasKeycodeLocked(device, AKEYCODE_DPAD_DOWN) &&
@@ -1205,14 +1210,6 @@
             }
         }
 
-        // 'Q' key support = cheap test of whether this is an alpha-capable kbd. Many gamepads will
-        // report a broader set of HID usages than they need, however, so we only want to mark this
-        // device as a keyboard if it is not a gamepad.
-        if (hasKeycodeLocked(device, AKEYCODE_Q) &&
-                !(device->classes & INPUT_DEVICE_CLASS_GAMEPAD)) {
-            device->classes |= INPUT_DEVICE_CLASS_ALPHAKEY;
-        }
-
         // Disable kernel key repeat since we handle it ourselves
         unsigned int repeatRate[] = {0,0};
         if (ioctl(fd, EVIOCSREP, repeatRate)) {
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 5b64d8a..07731fc 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -2631,6 +2631,7 @@
             info->addMotionRange(AMOTION_EVENT_AXIS_GENERIC_4, mSource, y.min, y.max, y.flat,
                     y.fuzz, y.resolution);
         }
+        info->setButtonUnderPad(mParameters.hasButtonUnderPad);
     }
 }
 
@@ -2796,6 +2797,9 @@
         mParameters.deviceType = Parameters::DEVICE_TYPE_POINTER;
     }
 
+    mParameters.hasButtonUnderPad=
+            getEventHub()->hasInputProperty(getDeviceId(), INPUT_PROP_BUTTONPAD);
+
     String8 deviceTypeString;
     if (getDevice()->getConfiguration().tryGetProperty(String8("touch.deviceType"),
             deviceTypeString)) {
@@ -4650,6 +4654,14 @@
                 mCurrentFingerIdBits, positions);
     }
 
+    // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
+    // to NEUTRAL, then we should not generate tap event.
+    if (mPointerGesture.lastGestureMode != PointerGesture::HOVER
+            && mPointerGesture.lastGestureMode != PointerGesture::TAP
+            && mPointerGesture.lastGestureMode != PointerGesture::TAP_DRAG) {
+        mPointerGesture.resetTap();
+    }
+
     // Pick a new active touch id if needed.
     // Choose an arbitrary pointer that just went down, if there is one.
     // Otherwise choose an arbitrary remaining pointer.
@@ -4858,8 +4870,12 @@
                 }
             } else {
 #if DEBUG_GESTURES
-                ALOGD("Gestures: Not a TAP, %0.3fms since down",
-                        (when - mPointerGesture.tapDownTime) * 0.000001f);
+                if (mPointerGesture.tapDownTime != LLONG_MIN) {
+                    ALOGD("Gestures: Not a TAP, %0.3fms since down",
+                            (when - mPointerGesture.tapDownTime) * 0.000001f);
+                } else {
+                    ALOGD("Gestures: Not a TAP, incompatible mode transitions");
+                }
 #endif
             }
         }
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 7e303e4..3ed426f 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -1205,6 +1205,7 @@
         bool hasAssociatedDisplay;
         bool associatedDisplayIsExternal;
         bool orientationAware;
+        bool hasButtonUnderPad;
 
         enum GestureMode {
             GESTURE_MODE_POINTER,
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java
index 0be9ca5..d7cdb9d 100644
--- a/services/java/com/android/server/AlarmManagerService.java
+++ b/services/java/com/android/server/AlarmManagerService.java
@@ -38,7 +38,6 @@
 import android.os.UserHandle;
 import android.os.WorkSource;
 import android.text.TextUtils;
-import android.text.format.Time;
 import android.util.Pair;
 import android.util.Slog;
 import android.util.TimeUtils;
@@ -57,31 +56,38 @@
 import java.util.Map;
 import java.util.TimeZone;
 
+import static android.app.AlarmManager.RTC_WAKEUP;
+import static android.app.AlarmManager.RTC;
+import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP;
+import static android.app.AlarmManager.ELAPSED_REALTIME;
+
 import com.android.internal.util.LocalLog;
 
 class AlarmManagerService extends IAlarmManager.Stub {
     // The threshold for how long an alarm can be late before we print a
     // warning message.  The time duration is in milliseconds.
     private static final long LATE_ALARM_THRESHOLD = 10 * 1000;
-    
-    private static final int RTC_WAKEUP_MASK = 1 << AlarmManager.RTC_WAKEUP;
-    private static final int RTC_MASK = 1 << AlarmManager.RTC;
-    private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << AlarmManager.ELAPSED_REALTIME_WAKEUP; 
-    private static final int ELAPSED_REALTIME_MASK = 1 << AlarmManager.ELAPSED_REALTIME;
+
+    private static final int RTC_WAKEUP_MASK = 1 << RTC_WAKEUP;
+    private static final int RTC_MASK = 1 << RTC;
+    private static final int ELAPSED_REALTIME_WAKEUP_MASK = 1 << ELAPSED_REALTIME_WAKEUP; 
+    private static final int ELAPSED_REALTIME_MASK = 1 << ELAPSED_REALTIME;
     private static final int TIME_CHANGED_MASK = 1 << 16;
     private static final int IS_WAKEUP_MASK = RTC_WAKEUP_MASK|ELAPSED_REALTIME_WAKEUP_MASK;
 
-    // Alignment quantum for inexact repeating alarms
-    private static final long QUANTUM = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
+    // Mask for testing whether a given alarm type is wakeup vs non-wakeup
+    private static final int TYPE_NONWAKEUP_MASK = 0x1; // low bit => non-wakeup
 
     private static final String TAG = "AlarmManager";
     private static final String ClockReceiver_TAG = "ClockReceiver";
     private static final boolean localLOGV = false;
+    private static final boolean DEBUG_BATCH = localLOGV || false;
     private static final int ALARM_EVENT = 1;
     private static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
     
     private static final Intent mBackgroundIntent
             = new Intent().addFlags(Intent.FLAG_FROM_BACKGROUND);
+    private static final IncreasingTimeOrder sIncreasingTimeOrder = new IncreasingTimeOrder();
     
     private static final boolean WAKEUP_STATS = true;
 
@@ -90,14 +96,10 @@
     private final LocalLog mLog = new LocalLog(TAG);
 
     private Object mLock = new Object();
-    
-    private final ArrayList<Alarm> mRtcWakeupAlarms = new ArrayList<Alarm>();
-    private final ArrayList<Alarm> mRtcAlarms = new ArrayList<Alarm>();
-    private final ArrayList<Alarm> mElapsedRealtimeWakeupAlarms = new ArrayList<Alarm>();
-    private final ArrayList<Alarm> mElapsedRealtimeAlarms = new ArrayList<Alarm>();
-    private final IncreasingTimeOrder mIncreasingTimeOrder = new IncreasingTimeOrder();
-    
+
     private int mDescriptor;
+    private long mNextWakeup;
+    private long mNextNonWakeup;
     private int mBroadcastRefCount = 0;
     private PowerManager.WakeLock mWakeLock;
     private ArrayList<InFlight> mInFlight = new ArrayList<InFlight>();
@@ -124,6 +126,277 @@
     private final LinkedList<WakeupEvent> mRecentWakeups = new LinkedList<WakeupEvent>();
     private final long RECENT_WAKEUP_PERIOD = 1000L * 60 * 60 * 24; // one day
 
+    static final class Batch {
+        long start;     // These endpoints are always in ELAPSED
+        long end;
+        boolean standalone; // certain "batches" don't participate in coalescing
+
+        final ArrayList<Alarm> alarms = new ArrayList<Alarm>();
+
+        Batch() {
+            start = 0;
+            end = Long.MAX_VALUE;
+        }
+
+        Batch(Alarm seed) {
+            start = seed.whenElapsed;
+            end = seed.maxWhen;
+            alarms.add(seed);
+        }
+
+        int size() {
+            return alarms.size();
+        }
+
+        Alarm get(int index) {
+            return alarms.get(index);
+        }
+
+        boolean canHold(long whenElapsed, long maxWhen) {
+            return (end >= whenElapsed) && (start <= maxWhen);
+        }
+
+        boolean add(Alarm alarm) {
+            boolean newStart = false;
+            // narrows the batch if necessary; presumes that canHold(alarm) is true
+            int index = Collections.binarySearch(alarms, alarm, sIncreasingTimeOrder);
+            if (index < 0) {
+                index = 0 - index - 1;
+            }
+            alarms.add(index, alarm);
+            if (DEBUG_BATCH) {
+                Slog.v(TAG, "Adding " + alarm + " to " + this);
+            }
+            if (alarm.whenElapsed > start) {
+                start = alarm.whenElapsed;
+                newStart = true;
+            }
+            if (alarm.maxWhen < end) {
+                end = alarm.maxWhen;
+            }
+
+            if (DEBUG_BATCH) {
+                Slog.v(TAG, "    => now " + this);
+            }
+            return newStart;
+        }
+
+        boolean remove(final PendingIntent operation) {
+            boolean didRemove = false;
+            long newStart = 0;  // recalculate endpoints as we go
+            long newEnd = Long.MAX_VALUE;
+            for (int i = 0; i < alarms.size(); i++) {
+                Alarm alarm = alarms.get(i);
+                if (alarm.operation.equals(operation)) {
+                    alarms.remove(i);
+                    didRemove = true;
+                } else {
+                    if (alarm.whenElapsed > newStart) {
+                        newStart = alarm.whenElapsed;
+                    }
+                    if (alarm.maxWhen < newEnd) {
+                        newEnd = alarm.maxWhen;
+                    }
+                    i++;
+                }
+            }
+            if (didRemove) {
+                // commit the new batch bounds
+                start = newStart;
+                end = newEnd;
+            }
+            return didRemove;
+        }
+
+        boolean remove(final String packageName) {
+            boolean didRemove = false;
+            long newStart = 0;  // recalculate endpoints as we go
+            long newEnd = Long.MAX_VALUE;
+            for (int i = 0; i < alarms.size(); i++) {
+                Alarm alarm = alarms.get(i);
+                if (alarm.operation.getTargetPackage().equals(packageName)) {
+                    alarms.remove(i);
+                    didRemove = true;
+                } else {
+                    if (alarm.whenElapsed > newStart) {
+                        newStart = alarm.whenElapsed;
+                    }
+                    if (alarm.maxWhen < newEnd) {
+                        newEnd = alarm.maxWhen;
+                    }
+                    i++;
+                }
+            }
+            if (didRemove) {
+                // commit the new batch bounds
+                start = newStart;
+                end = newEnd;
+            }
+            return didRemove;
+        }
+
+        boolean remove(final int userHandle) {
+            boolean didRemove = false;
+            long newStart = 0;  // recalculate endpoints as we go
+            long newEnd = Long.MAX_VALUE;
+            for (int i = 0; i < alarms.size(); i++) {
+                Alarm alarm = alarms.get(i);
+                if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) {
+                    alarms.remove(i);
+                    didRemove = true;
+                } else {
+                    if (alarm.whenElapsed > newStart) {
+                        newStart = alarm.whenElapsed;
+                    }
+                    if (alarm.maxWhen < newEnd) {
+                        newEnd = alarm.maxWhen;
+                    }
+                    i++;
+                }
+            }
+            if (didRemove) {
+                // commit the new batch bounds
+                start = newStart;
+                end = newEnd;
+            }
+            return didRemove;
+        }
+
+        boolean hasPackage(final String packageName) {
+            final int N = alarms.size();
+            for (int i = 0; i < N; i++) {
+                Alarm a = alarms.get(i);
+                if (a.operation.getTargetPackage().equals(packageName)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        boolean hasWakeups() {
+            final int N = alarms.size();
+            for (int i = 0; i < N; i++) {
+                Alarm a = alarms.get(i);
+                // non-wakeup alarms are types 1 and 3, i.e. have the low bit set
+                if ((a.type & TYPE_NONWAKEUP_MASK) == 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder b = new StringBuilder(40);
+            b.append("Batch{"); b.append(Integer.toHexString(this.hashCode()));
+            b.append(" num="); b.append(size());
+            b.append(" start="); b.append(start);
+            b.append(" end="); b.append(end);
+            if (standalone) {
+                b.append(" STANDALONE");
+            }
+            b.append('}');
+            return b.toString();
+        }
+    }
+
+    static class BatchTimeOrder implements Comparator<Batch> {
+        public int compare(Batch b1, Batch b2) {
+            long when1 = b1.start;
+            long when2 = b2.start;
+            if (when1 - when2 > 0) {
+                return 1;
+            }
+            if (when1 - when2 < 0) {
+                return -1;
+            }
+            return 0;
+        }
+    }
+    
+    // minimum recurrence period or alarm futurity for us to be able to fuzz it
+    private static final long MIN_FUZZABLE_INTERVAL = 10000;
+    private static final BatchTimeOrder sBatchOrder = new BatchTimeOrder();
+    private final ArrayList<Batch> mAlarmBatches = new ArrayList<Batch>();
+
+    static long convertToElapsed(long when, int type) {
+        final boolean isRtc = (type == RTC || type == RTC_WAKEUP);
+        if (isRtc) {
+            when -= System.currentTimeMillis() - SystemClock.elapsedRealtime();
+        }
+        return when;
+    }
+
+    // Apply a heuristic to { recurrence interval, futurity of the trigger time } to
+    // calculate the end of our nominal delivery window for the alarm.
+    static long maxTriggerTime(long now, long triggerAtTime, long interval) {
+        // Current heuristic: batchable window is 75% of either the recurrence interval
+        // [for a periodic alarm] or of the time from now to the desired delivery time,
+        // with a minimum delay/interval of 10 seconds, under which we will simply not
+        // defer the alarm.
+        long futurity = (interval == 0)
+                ? (triggerAtTime - now)
+                : interval;
+        if (futurity < MIN_FUZZABLE_INTERVAL) {
+            futurity = 0;
+        }
+        return triggerAtTime + (long)(.75 * futurity);
+    }
+
+    // returns true if the batch was added at the head
+    static boolean addBatchLocked(ArrayList<Batch> list, Batch newBatch) {
+        int index = Collections.binarySearch(list, newBatch, sBatchOrder);
+        if (index < 0) {
+            index = 0 - index - 1;
+        }
+        list.add(index, newBatch);
+        return (index == 0);
+    }
+
+    Batch attemptCoalesceLocked(long whenElapsed, long maxWhen) {
+        final int N = mAlarmBatches.size();
+        for (int i = 0; i < N; i++) {
+            Batch b = mAlarmBatches.get(i);
+            if (!b.standalone && b.canHold(whenElapsed, maxWhen)) {
+                return b;
+            }
+        }
+        return null;
+    }
+
+    // The RTC clock has moved arbitrarily, so we need to recalculate all the batching
+    void rebatchAllAlarms() {
+        if (DEBUG_BATCH) {
+            Slog.v(TAG, "RTC changed; rebatching");
+        }
+        synchronized (mLock) {
+            ArrayList<Batch> oldSet = (ArrayList<Batch>) mAlarmBatches.clone();
+            mAlarmBatches.clear();
+            final long nowElapsed = SystemClock.elapsedRealtime();
+            for (Batch batch : oldSet) {
+                final int N = batch.size();
+                for (int i = 0; i < N; i++) {
+                    Alarm a = batch.get(i);
+                    long whenElapsed = convertToElapsed(a.when, a.type);
+                    long maxElapsed = (a.whenElapsed == a.maxWhen)
+                            ? whenElapsed
+                            : maxTriggerTime(nowElapsed, whenElapsed, a.repeatInterval);
+                    if (batch.standalone) {
+                        // this will also be the only alarm in the batch
+                        a = new Alarm(a.type, a.when, whenElapsed, maxElapsed,
+                                a.repeatInterval, a.operation);
+                        Batch newBatch = new Batch(a);
+                        newBatch.standalone = true;
+                        addBatchLocked(mAlarmBatches, newBatch);
+                    } else {
+                        setImplLocked(a.type, a.when, whenElapsed, maxElapsed,
+                                a.repeatInterval, a.operation, false);
+                    }
+                }
+            }
+        }
+    }
+
     private static final class InFlight extends Intent {
         final PendingIntent mPendingIntent;
         final Pair<String, ComponentName> mTarget;
@@ -184,6 +457,7 @@
     public AlarmManagerService(Context context) {
         mContext = context;
         mDescriptor = init();
+        mNextWakeup = mNextNonWakeup = 0;
 
         // We have to set current TimeZone info to kernel
         // because kernel doesn't keep this after reboot
@@ -224,77 +498,102 @@
             super.finalize();
         }
     }
-    
-    public void set(int type, long triggerAtTime, PendingIntent operation) {
-        setRepeating(type, triggerAtTime, 0, operation);
-    }
-    
-    public void setRepeating(int type, long triggerAtTime, long interval, 
+
+    @Override
+    public void set(int type, long triggerAtTime, long windowLength, long interval,
             PendingIntent operation) {
+        set(type, triggerAtTime, windowLength, interval, operation, false);
+    }
+
+    public void set(int type, long triggerAtTime, long windowLength, long interval,
+            PendingIntent operation, boolean isStandalone) {
         if (operation == null) {
             Slog.w(TAG, "set/setRepeating ignored because there is no intent");
             return;
         }
+
+        // Sanity check the window length.  This will catch people mistakenly
+        // trying to pass an end-of-window timestamp rather than a duration.
+        if (windowLength > AlarmManager.INTERVAL_HALF_DAY) {
+            Slog.w(TAG, "Window length " + windowLength
+                    + "ms suspiciously long; limiting to 1 hour");
+            windowLength = AlarmManager.INTERVAL_HOUR;
+        }
+
+        if (type < RTC_WAKEUP || type > ELAPSED_REALTIME) {
+            throw new IllegalArgumentException("Invalid alarm type " + type);
+        }
+
+        final long nowElapsed = SystemClock.elapsedRealtime();
+        final long triggerElapsed = convertToElapsed(triggerAtTime, type);
+        final long maxElapsed;
+        if (windowLength == AlarmManager.WINDOW_EXACT) {
+            maxElapsed = triggerElapsed;
+        } else if (windowLength < 0) {
+            maxElapsed = maxTriggerTime(nowElapsed, triggerElapsed, interval);
+        } else {
+            maxElapsed = triggerElapsed + windowLength;
+        }
+
         synchronized (mLock) {
-            Alarm alarm = new Alarm();
-            alarm.type = type;
-            alarm.when = triggerAtTime;
-            alarm.repeatInterval = interval;
-            alarm.operation = operation;
-
-            // Remove this alarm if already scheduled.
-            removeLocked(operation);
-
-            if (localLOGV) Slog.v(TAG, "set: " + alarm);
-
-            int index = addAlarmLocked(alarm);
-            if (index == 0) {
-                setLocked(alarm);
+            if (DEBUG_BATCH) {
+                Slog.v(TAG, "set(" + operation + ") : type=" + type
+                        + " triggerAtTime=" + triggerAtTime + " win=" + windowLength
+                        + " tElapsed=" + triggerElapsed + " maxElapsed=" + maxElapsed
+                        + " interval=" + interval + " standalone=" + isStandalone);
             }
+            setImplLocked(type, triggerAtTime, triggerElapsed, maxElapsed,
+                    interval, operation, isStandalone);
         }
     }
-    
-    public void setInexactRepeating(int type, long triggerAtTime, long interval, 
-            PendingIntent operation) {
-        if (operation == null) {
-            Slog.w(TAG, "setInexactRepeating ignored because there is no intent");
-            return;
-        }
 
-        if (interval <= 0) {
-            Slog.w(TAG, "setInexactRepeating ignored because interval " + interval
-                    + " is invalid");
-            return;
-        }
+    private void setImplLocked(int type, long when, long whenElapsed, long maxWhen, long interval,
+            PendingIntent operation, boolean isStandalone) {
+        Alarm a = new Alarm(type, when, whenElapsed, maxWhen, interval, operation);
+        removeLocked(operation);
 
-        // If the requested interval isn't a multiple of 15 minutes, just treat it as exact
-        if (interval % QUANTUM != 0) {
-            if (localLOGV) Slog.v(TAG, "Interval " + interval + " not a quantum multiple");
-            setRepeating(type, triggerAtTime, interval, operation);
-            return;
-        }
-
-        // Translate times into the ELAPSED timebase for alignment purposes so that
-        // alignment never tries to match against wall clock times.
-        final boolean isRtc = (type == AlarmManager.RTC || type == AlarmManager.RTC_WAKEUP);
-        final long skew = (isRtc)
-                ? System.currentTimeMillis() - SystemClock.elapsedRealtime()
-                : 0;
-
-        // Slip forward to the next ELAPSED-timebase quantum after the stated time.  If
-        // we're *at* a quantum point, leave it alone.
-        final long adjustedTriggerTime;
-        long offset = (triggerAtTime - skew) % QUANTUM;
-        if (offset != 0) {
-            adjustedTriggerTime = triggerAtTime - offset + QUANTUM;
+        final boolean reschedule;
+        Batch batch = (isStandalone) ? null : attemptCoalesceLocked(whenElapsed, maxWhen);
+        if (batch == null) {
+            batch = new Batch(a);
+            batch.standalone = isStandalone;
+            if (DEBUG_BATCH) {
+                Slog.v(TAG, "Starting new alarm batch " + batch);
+            }
+            reschedule = addBatchLocked(mAlarmBatches, batch);
         } else {
-            adjustedTriggerTime = triggerAtTime;
+            reschedule = batch.add(a);
         }
 
-        // Set the alarm based on the quantum-aligned start time
-        if (localLOGV) Slog.v(TAG, "setInexactRepeating: type=" + type + " interval=" + interval
-                + " trigger=" + adjustedTriggerTime + " orig=" + triggerAtTime);
-        setRepeating(type, adjustedTriggerTime, interval, operation);
+        if (reschedule) {
+            rescheduleKernelAlarmsLocked();
+        }
+    }
+
+    private Batch findFirstWakeupBatchLocked() {
+        final int N = mAlarmBatches.size();
+        for (int i = 0; i < N; i++) {
+            Batch b = mAlarmBatches.get(i);
+            if (b.hasWakeups()) {
+                return b;
+            }
+        }
+        return null;
+    }
+
+    private void rescheduleKernelAlarmsLocked() {
+        // Schedule the next upcoming wakeup alarm.  If there is a deliverable batch
+        // prior to that which contains no wakeups, we schedule that as well.
+        final Batch firstWakeup = findFirstWakeupBatchLocked();
+        final Batch firstBatch = mAlarmBatches.get(0);
+        if (firstWakeup != null && mNextWakeup != firstWakeup.start) {
+            mNextWakeup = firstWakeup.start;
+            setLocked(ELAPSED_REALTIME_WAKEUP, firstWakeup.start);
+        }
+        if (firstBatch != firstWakeup && mNextNonWakeup != firstBatch.start) {
+            mNextNonWakeup = firstBatch.start;
+            setLocked(ELAPSED_REALTIME, firstBatch.start);
+        }
     }
 
     public void setTime(long millis) {
@@ -356,160 +655,61 @@
     }
     
     public void removeLocked(PendingIntent operation) {
-        removeLocked(mRtcWakeupAlarms, operation);
-        removeLocked(mRtcAlarms, operation);
-        removeLocked(mElapsedRealtimeWakeupAlarms, operation);
-        removeLocked(mElapsedRealtimeAlarms, operation);
-    }
-
-    private void removeLocked(ArrayList<Alarm> alarmList,
-            PendingIntent operation) {
-        if (alarmList.size() <= 0) {
-            return;
-        }
-
-        // iterator over the list removing any it where the intent match
-        for (int i=0; i<alarmList.size(); i++) {
-            Alarm alarm = alarmList.get(i);
-            if (alarm.operation.equals(operation)) {
-                alarmList.remove(i);
-                i--;
+        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
+            Batch b = mAlarmBatches.get(i);
+            b.remove(operation);
+            if (b.size() == 0) {
+                mAlarmBatches.remove(i);
             }
         }
     }
 
     public void removeLocked(String packageName) {
-        removeLocked(mRtcWakeupAlarms, packageName);
-        removeLocked(mRtcAlarms, packageName);
-        removeLocked(mElapsedRealtimeWakeupAlarms, packageName);
-        removeLocked(mElapsedRealtimeAlarms, packageName);
-    }
-
-    private void removeLocked(ArrayList<Alarm> alarmList,
-            String packageName) {
-        if (alarmList.size() <= 0) {
-            return;
-        }
-
-        // iterator over the list removing any it where the intent match
-        for (int i=0; i<alarmList.size(); i++) {
-            Alarm alarm = alarmList.get(i);
-            if (alarm.operation.getTargetPackage().equals(packageName)) {
-                alarmList.remove(i);
-                i--;
+        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
+            Batch b = mAlarmBatches.get(i);
+            b.remove(packageName);
+            if (b.size() == 0) {
+                mAlarmBatches.remove(i);
             }
         }
     }
 
     public void removeUserLocked(int userHandle) {
-        removeUserLocked(mRtcWakeupAlarms, userHandle);
-        removeUserLocked(mRtcAlarms, userHandle);
-        removeUserLocked(mElapsedRealtimeWakeupAlarms, userHandle);
-        removeUserLocked(mElapsedRealtimeAlarms, userHandle);
-    }
-
-    private void removeUserLocked(ArrayList<Alarm> alarmList, int userHandle) {
-        if (alarmList.size() <= 0) {
-            return;
-        }
-
-        // iterator over the list removing any it where the intent match
-        for (int i=0; i<alarmList.size(); i++) {
-            Alarm alarm = alarmList.get(i);
-            if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) {
-                alarmList.remove(i);
-                i--;
+        for (int i = mAlarmBatches.size() - 1; i >= 0; i--) {
+            Batch b = mAlarmBatches.get(i);
+            b.remove(userHandle);
+            if (b.size() == 0) {
+                mAlarmBatches.remove(i);
             }
         }
     }
-    
-    public boolean lookForPackageLocked(String packageName) {
-        return lookForPackageLocked(mRtcWakeupAlarms, packageName)
-                || lookForPackageLocked(mRtcAlarms, packageName)
-                || lookForPackageLocked(mElapsedRealtimeWakeupAlarms, packageName)
-                || lookForPackageLocked(mElapsedRealtimeAlarms, packageName);
-    }
 
-    private boolean lookForPackageLocked(ArrayList<Alarm> alarmList, String packageName) {
-        for (int i=alarmList.size()-1; i>=0; i--) {
-            if (alarmList.get(i).operation.getTargetPackage().equals(packageName)) {
+    public boolean lookForPackageLocked(String packageName) {
+        for (int i = 0; i < mAlarmBatches.size(); i++) {
+            Batch b = mAlarmBatches.get(i);
+            if (b.hasPackage(packageName)) {
                 return true;
             }
         }
         return false;
     }
-    
-    private ArrayList<Alarm> getAlarmList(int type) {
-        switch (type) {
-            case AlarmManager.RTC_WAKEUP:              return mRtcWakeupAlarms;
-            case AlarmManager.RTC:                     return mRtcAlarms;
-            case AlarmManager.ELAPSED_REALTIME_WAKEUP: return mElapsedRealtimeWakeupAlarms;
-            case AlarmManager.ELAPSED_REALTIME:        return mElapsedRealtimeAlarms;
-        }
-        
-        return null;
-    }
-    
-    private int addAlarmLocked(Alarm alarm) {
-        ArrayList<Alarm> alarmList = getAlarmList(alarm.type);
-        
-        int index = Collections.binarySearch(alarmList, alarm, mIncreasingTimeOrder);
-        if (index < 0) {
-            index = 0 - index - 1;
-        }
-        if (localLOGV) Slog.v(TAG, "Adding alarm " + alarm + " at " + index);
-        alarmList.add(index, alarm);
 
-        if (localLOGV) {
-            // Display the list of alarms for this alarm type
-            Slog.v(TAG, "alarms: " + alarmList.size() + " type: " + alarm.type);
-            int position = 0;
-            for (Alarm a : alarmList) {
-                Time time = new Time();
-                time.set(a.when);
-                String timeStr = time.format("%b %d %I:%M:%S %p");
-                Slog.v(TAG, position + ": " + timeStr
-                        + " " + a.operation.getTargetPackage());
-                position += 1;
-            }
-        }
-        
-        return index;
-    }
-    
-    public long timeToNextAlarm() {
-        long nextAlarm = Long.MAX_VALUE;
-        synchronized (mLock) {
-            for (int i=AlarmManager.RTC_WAKEUP;
-                    i<=AlarmManager.ELAPSED_REALTIME; i++) {
-                ArrayList<Alarm> alarmList = getAlarmList(i);
-                if (alarmList.size() > 0) {
-                    Alarm a = alarmList.get(0);
-                    if (a.when < nextAlarm) {
-                        nextAlarm = a.when;
-                    }
-                }
-            }
-        }
-        return nextAlarm;
-    }
-    
-    private void setLocked(Alarm alarm)
+    private void setLocked(int type, long when)
     {
         if (mDescriptor != -1)
         {
             // The kernel never triggers alarms with negative wakeup times
             // so we ensure they are positive.
             long alarmSeconds, alarmNanoseconds;
-            if (alarm.when < 0) {
+            if (when < 0) {
                 alarmSeconds = 0;
                 alarmNanoseconds = 0;
             } else {
-                alarmSeconds = alarm.when / 1000;
-                alarmNanoseconds = (alarm.when % 1000) * 1000 * 1000;
+                alarmSeconds = when / 1000;
+                alarmNanoseconds = (when % 1000) * 1000 * 1000;
             }
             
-            set(mDescriptor, alarm.type, alarmSeconds, alarmNanoseconds);
+            set(mDescriptor, type, alarmSeconds, alarmNanoseconds);
         }
         else
         {
@@ -517,7 +717,7 @@
             msg.what = ALARM_EVENT;
             
             mHandler.removeMessages(ALARM_EVENT);
-            mHandler.sendMessageAtTime(msg, alarm.when);
+            mHandler.sendMessageAtTime(msg, when);
         }
     }
     
@@ -533,29 +733,28 @@
         
         synchronized (mLock) {
             pw.println("Current Alarm Manager state:");
-            if (mRtcWakeupAlarms.size() > 0 || mRtcAlarms.size() > 0) {
-                final long now = System.currentTimeMillis();
-                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
-                pw.println(" ");
-                pw.print("  Realtime wakeup (now=");
-                        pw.print(sdf.format(new Date(now))); pw.println("):");
-                if (mRtcWakeupAlarms.size() > 0) {
-                    dumpAlarmList(pw, mRtcWakeupAlarms, "  ", "RTC_WAKEUP", now);
-                }
-                if (mRtcAlarms.size() > 0) {
-                    dumpAlarmList(pw, mRtcAlarms, "  ", "RTC", now);
-                }
-            }
-            if (mElapsedRealtimeWakeupAlarms.size() > 0 || mElapsedRealtimeAlarms.size() > 0) {
-                final long now = SystemClock.elapsedRealtime();
-                pw.println(" ");
-                pw.print("  Elapsed realtime wakeup (now=");
-                        TimeUtils.formatDuration(now, pw); pw.println("):");
-                if (mElapsedRealtimeWakeupAlarms.size() > 0) {
-                    dumpAlarmList(pw, mElapsedRealtimeWakeupAlarms, "  ", "ELAPSED_WAKEUP", now);
-                }
-                if (mElapsedRealtimeAlarms.size() > 0) {
-                    dumpAlarmList(pw, mElapsedRealtimeAlarms, "  ", "ELAPSED", now);
+            final long nowRTC = System.currentTimeMillis();
+            final long nowELAPSED = SystemClock.elapsedRealtime();
+            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+
+            pw.print("nowRTC="); pw.print(nowRTC);
+            pw.print("="); pw.print(sdf.format(new Date(nowRTC)));
+            pw.print(" nowELAPSED="); pw.println(nowELAPSED);
+
+            long nextWakeupRTC = mNextWakeup + (nowRTC - nowELAPSED);
+            long nextNonWakeupRTC = mNextNonWakeup + (nowRTC - nowELAPSED);
+            pw.print("Next alarm: "); pw.print(mNextNonWakeup);
+                    pw.print(" = "); pw.println(sdf.format(new Date(nextNonWakeupRTC)));
+            pw.print("Next wakeup: "); pw.print(mNextWakeup);
+                    pw.print(" = "); pw.println(sdf.format(new Date(nextWakeupRTC)));
+
+            if (mAlarmBatches.size() > 0) {
+                pw.println();
+                pw.print("Pending alarm batches: ");
+                pw.println(mAlarmBatches.size());
+                for (Batch b : mAlarmBatches) {
+                    pw.print(b); pw.println(':');
+                    dumpAlarmList(pw, b.alarms, "  ", nowELAPSED, nowRTC);
                 }
             }
 
@@ -662,7 +861,6 @@
             if (WAKEUP_STATS) {
                 pw.println();
                 pw.println("  Recent Wakeup History:");
-                final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss.SSS");
                 long last = -1;
                 for (WakeupEvent event : mRecentWakeups) {
                     pw.print("    "); pw.print(sdf.format(new Date(event.when)));
@@ -691,77 +889,78 @@
             a.dump(pw, prefix + "  ", now);
         }
     }
-    
+
+    private static final String labelForType(int type) {
+        switch (type) {
+        case RTC: return "RTC";
+        case RTC_WAKEUP : return "RTC_WAKEUP";
+        case ELAPSED_REALTIME : return "ELAPSED";
+        case ELAPSED_REALTIME_WAKEUP: return "ELAPSED_WAKEUP";
+        default:
+            break;
+        }
+        return "--unknown--";
+    }
+
+    private static final void dumpAlarmList(PrintWriter pw, ArrayList<Alarm> list,
+            String prefix, long nowELAPSED, long nowRTC) {
+        for (int i=list.size()-1; i>=0; i--) {
+            Alarm a = list.get(i);
+            final String label = labelForType(a.type);
+            long now = (a.type <= RTC) ? nowRTC : nowELAPSED;
+            pw.print(prefix); pw.print(label); pw.print(" #"); pw.print(i);
+                    pw.print(": "); pw.println(a);
+            a.dump(pw, prefix + "  ", now);
+        }
+    }
+
     private native int init();
     private native void close(int fd);
     private native void set(int fd, int type, long seconds, long nanoseconds);
     private native int waitForAlarm(int fd);
     private native int setKernelTimezone(int fd, int minuteswest);
 
-    private void triggerAlarmsLocked(ArrayList<Alarm> alarmList,
-                                     ArrayList<Alarm> triggerList,
-                                     long now)
-    {
-        ArrayList<Alarm> repeats = null;
+    private void triggerAlarmsLocked(ArrayList<Alarm> triggerList, long nowELAPSED, long nowRTC) {
+        Batch batch;
 
-        for (int i=0; i<alarmList.size(); i++) {
-            Alarm alarm = alarmList.get(i);
-
-            if (localLOGV) Slog.v(TAG, "Checking active alarm when=" + alarm.when + " " + alarm);
-
-            if (alarm.when > now) {
-                // don't fire alarms in the future
+        // batches are temporally sorted, so we need only pull from the
+        // start of the list until we either empty it or hit a batch
+        // that is not yet deliverable
+        while ((batch = mAlarmBatches.get(0)) != null) {
+            if (batch.start > nowELAPSED) {
+                // Everything else is scheduled for the future
                 break;
             }
-            
-            // If the alarm is late, then print a warning message.
-            // Note that this can happen if the user creates a new event on
-            // the Calendar app with a reminder that is in the past. In that
-            // case, the reminder alarm will fire immediately.
-            if (localLOGV && now - alarm.when > LATE_ALARM_THRESHOLD) {
-                Slog.v(TAG, "alarm is late! alarm time: " + alarm.when
-                        + " now: " + now + " delay (in seconds): "
-                        + (now - alarm.when) / 1000);
-            }
 
-            // Recurring alarms may have passed several alarm intervals while the
-            // phone was asleep or off, so pass a trigger count when sending them.
-            if (localLOGV) Slog.v(TAG, "Alarm triggering: " + alarm);
-            alarm.count = 1;
-            if (alarm.repeatInterval > 0) {
-                // this adjustment will be zero if we're late by
-                // less than one full repeat interval
-                alarm.count += (now - alarm.when) / alarm.repeatInterval;
-            }
-            triggerList.add(alarm);
-            
-            // remove the alarm from the list
-            alarmList.remove(i);
-            i--;
+            // We will (re)schedule some alarms now; don't let that interfere
+            // with delivery of this current batch
+            mAlarmBatches.remove(0);
 
-            // if it repeats queue it up to be read-added to the list
-            if (alarm.repeatInterval > 0) {
-                if (repeats == null) {
-                    repeats = new ArrayList<Alarm>();
+            final int N = batch.size();
+            for (int i = 0; i < N; i++) {
+                Alarm alarm = batch.get(i);
+                alarm.count = 1;
+                triggerList.add(alarm);
+
+                // Recurring alarms may have passed several alarm intervals while the
+                // phone was asleep or off, so pass a trigger count when sending them.
+                if (alarm.repeatInterval > 0) {
+                    // this adjustment will be zero if we're late by
+                    // less than one full repeat interval
+                    alarm.count += (nowELAPSED - alarm.whenElapsed) / alarm.repeatInterval;
+
+                    // Also schedule its next recurrence
+                    final long delta = alarm.count * alarm.repeatInterval;
+                    final long nextElapsed = alarm.whenElapsed + delta;
+                    setImplLocked(alarm.type, alarm.when + delta, nextElapsed,
+                            maxTriggerTime(nowELAPSED, nextElapsed, alarm.repeatInterval),
+                            alarm.repeatInterval, alarm.operation, batch.standalone);
                 }
-                repeats.add(alarm);
-            }
-        }
 
-        // reset any repeating alarms.
-        if (repeats != null) {
-            for (int i=0; i<repeats.size(); i++) {
-                Alarm alarm = repeats.get(i);
-                alarm.when += alarm.count * alarm.repeatInterval;
-                addAlarmLocked(alarm);
             }
         }
-        
-        if (alarmList.size() > 0) {
-            setLocked(alarmList.get(0));
-        }
     }
-    
+
     /**
      * This Comparator sorts Alarms into increasing time order.
      */
@@ -783,15 +982,21 @@
         public int type;
         public int count;
         public long when;
+        public long whenElapsed;    // 'when' in the elapsed time base
+        public long maxWhen;        // also in the elapsed time base
         public long repeatInterval;
         public PendingIntent operation;
         
-        public Alarm() {
-            when = 0;
-            repeatInterval = 0;
-            operation = null;
+        public Alarm(int _type, long _when, long _whenElapsed, long _maxWhen,
+                long _interval, PendingIntent _op) {
+            type = _type;
+            when = _when;
+            whenElapsed = _whenElapsed;
+            maxWhen = _maxWhen;
+            repeatInterval = _interval;
+            operation = _op;
         }
-        
+
         @Override
         public String toString()
         {
@@ -808,6 +1013,7 @@
 
         public void dump(PrintWriter pw, String prefix, long now) {
             pw.print(prefix); pw.print("type="); pw.print(type);
+                    pw.print(" whenElapsed="); pw.print(whenElapsed);
                     pw.print(" when="); TimeUtils.formatDuration(when, now, pw);
                     pw.print(" repeatInterval="); pw.print(repeatInterval);
                     pw.print(" count="); pw.println(count);
@@ -815,16 +1021,22 @@
         }
     }
 
-    void recordWakeupAlarms(ArrayList<Alarm> alarms, long now, long skewToRTC) {
-        for (Alarm a : alarms) {
-            if (a.when > now) {
+    void recordWakeupAlarms(ArrayList<Batch> batches, long nowELAPSED, long nowRTC) {
+        final int numBatches = batches.size();
+        for (int nextBatch = 0; nextBatch < numBatches; nextBatch++) {
+            Batch b = batches.get(nextBatch);
+            if (b.start > nowELAPSED) {
                 break;
             }
 
-            WakeupEvent e = new WakeupEvent(now + skewToRTC,
-                    a.operation.getCreatorUid(),
-                    a.operation.getIntent().getAction());
-            mRecentWakeups.add(e);
+            final int numAlarms = b.alarms.size();
+            for (int nextAlarm = 0; nextAlarm < numAlarms; nextAlarm++) {
+                Alarm a = b.alarms.get(nextAlarm);
+                WakeupEvent e = new WakeupEvent(nowRTC,
+                        a.operation.getCreatorUid(),
+                        a.operation.getIntent().getAction());
+                mRecentWakeups.add(e);
+            }
         }
     }
 
@@ -847,6 +1059,7 @@
 
                 if ((result & TIME_CHANGED_MASK) != 0) {
                     remove(mTimeTickSender);
+                    rebatchAllAlarms();
                     mClockReceiver.scheduleTimeTickEvent();
                     Intent intent = new Intent(Intent.ACTION_TIME_CHANGED);
                     intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
@@ -873,28 +1086,14 @@
                                 mRecentWakeups.remove();
                             }
 
-                            recordWakeupAlarms(mRtcWakeupAlarms,
-                                    nowRTC,
-                                    0);
-                            recordWakeupAlarms(mElapsedRealtimeWakeupAlarms,
-                                    nowELAPSED,
-                                    nowRTC - nowELAPSED);
+                            recordWakeupAlarms(mAlarmBatches, nowELAPSED, nowRTC);
                         }
                     }
 
-                    if ((result & RTC_WAKEUP_MASK) != 0)
-                        triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
-                    
-                    if ((result & RTC_MASK) != 0)
-                        triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
-                    
-                    if ((result & ELAPSED_REALTIME_WAKEUP_MASK) != 0)
-                        triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowELAPSED);
-                    
-                    if ((result & ELAPSED_REALTIME_MASK) != 0)
-                        triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowELAPSED);
-                    
-                    // now trigger the alarms
+                    triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
+                    rescheduleKernelAlarmsLocked();
+
+                    // now deliver the alarm intents
                     for (int i=0; i<triggerList.size(); i++) {
                         Alarm alarm = triggerList.get(i);
                         try {
@@ -930,8 +1129,8 @@
                             } else {
                                 fs.nesting++;
                             }
-                            if (alarm.type == AlarmManager.ELAPSED_REALTIME_WAKEUP
-                                    || alarm.type == AlarmManager.RTC_WAKEUP) {
+                            if (alarm.type == ELAPSED_REALTIME_WAKEUP
+                                    || alarm.type == RTC_WAKEUP) {
                                 bs.numWakeup++;
                                 fs.numWakeup++;
                                 ActivityManagerNative.noteWakeupAlarm(
@@ -980,10 +1179,8 @@
                 ArrayList<Alarm> triggerList = new ArrayList<Alarm>();
                 synchronized (mLock) {
                     final long nowRTC = System.currentTimeMillis();
-                    triggerAlarmsLocked(mRtcWakeupAlarms, triggerList, nowRTC);
-                    triggerAlarmsLocked(mRtcAlarms, triggerList, nowRTC);
-                    triggerAlarmsLocked(mElapsedRealtimeWakeupAlarms, triggerList, nowRTC);
-                    triggerAlarmsLocked(mElapsedRealtimeAlarms, triggerList, nowRTC);
+                    final long nowELAPSED = SystemClock.elapsedRealtime();
+                    triggerAlarmsLocked(triggerList, nowELAPSED, nowRTC);
                 }
                 
                 // now trigger the alarms without the lock held
@@ -1035,8 +1232,8 @@
             // the top of the next minute.
             final long tickEventDelay = nextTime - currentTime;
 
-            set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay,
-                    mTimeTickSender);
+            set(ELAPSED_REALTIME, SystemClock.elapsedRealtime() + tickEventDelay, 0,
+                    0, mTimeTickSender, true);
         }
 	
         public void scheduleDateChangedEvent() {
@@ -1048,7 +1245,7 @@
             calendar.set(Calendar.MILLISECOND, 0);
             calendar.add(Calendar.DAY_OF_MONTH, 1);
       
-            set(AlarmManager.RTC, calendar.getTimeInMillis(), mDateChangeSender);
+            set(RTC, calendar.getTimeInMillis(), 0, 0, mDateChangeSender, true);
         }
     }
     
diff --git a/services/java/com/android/server/AppOpsService.java b/services/java/com/android/server/AppOpsService.java
index 7a107e7..6b4d248 100644
--- a/services/java/com/android/server/AppOpsService.java
+++ b/services/java/com/android/server/AppOpsService.java
@@ -40,6 +40,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.UserHandle;
+import android.util.ArrayMap;
 import android.util.AtomicFile;
 import android.util.Log;
 import android.util.Slog;
@@ -97,6 +98,8 @@
     }
 
     public final static class Op {
+        public final int uid;
+        public final String packageName;
         public final int op;
         public int mode;
         public int duration;
@@ -104,7 +107,9 @@
         public long rejectTime;
         public int nesting;
 
-        public Op(int _op) {
+        public Op(int _uid, String _packageName, int _op) {
+            uid = _uid;
+            packageName = _packageName;
             op = _op;
             mode = AppOpsManager.MODE_ALLOWED;
         }
@@ -112,10 +117,10 @@
 
     final SparseArray<ArrayList<Callback>> mOpModeWatchers
             = new SparseArray<ArrayList<Callback>>();
-    final HashMap<String, ArrayList<Callback>> mPackageModeWatchers
-            = new HashMap<String, ArrayList<Callback>>();
-    final HashMap<IBinder, Callback> mModeWatchers
-            = new HashMap<IBinder, Callback>();
+    final ArrayMap<String, ArrayList<Callback>> mPackageModeWatchers
+            = new ArrayMap<String, ArrayList<Callback>>();
+    final ArrayMap<IBinder, Callback> mModeWatchers
+            = new ArrayMap<IBinder, Callback>();
 
     public final class Callback implements DeathRecipient {
         final IAppOpsCallback mCallback;
@@ -138,6 +143,47 @@
         }
     }
 
+    final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<IBinder, ClientState>();
+
+    public final class ClientState extends Binder implements DeathRecipient {
+        final IBinder mAppToken;
+        final int mPid;
+        final ArrayList<Op> mStartedOps;
+
+        public ClientState(IBinder appToken) {
+            mAppToken = appToken;
+            mPid = Binder.getCallingPid();
+            if (appToken instanceof Binder) {
+                // For local clients, there is no reason to track them.
+                mStartedOps = null;
+            } else {
+                mStartedOps = new ArrayList<Op>();
+                try {
+                    mAppToken.linkToDeath(this, 0);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "ClientState{" +
+                    "mAppToken=" + mAppToken +
+                    ", " + (mStartedOps != null ? ("pid=" + mPid) : "local") +
+                    '}';
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (AppOpsService.this) {
+                for (int i=mStartedOps.size()-1; i>=0; i--) {
+                    finishOperationLocked(mStartedOps.get(i));
+                }
+                mClients.remove(mAppToken);
+            }
+        }
+    }
+
     public AppOpsService(File storagePath) {
         mFile = new AtomicFile(storagePath);
         mHandler = new Handler();
@@ -380,21 +426,18 @@
             Callback cb = mModeWatchers.remove(callback.asBinder());
             if (cb != null) {
                 cb.unlinkToDeath();
-                for (int i=0; i<mOpModeWatchers.size(); i++) {
+                for (int i=mOpModeWatchers.size()-1; i>=0; i--) {
                     ArrayList<Callback> cbs = mOpModeWatchers.valueAt(i);
                     cbs.remove(cb);
                     if (cbs.size() <= 0) {
                         mOpModeWatchers.removeAt(i);
                     }
                 }
-                if (mPackageModeWatchers.size() > 0) {
-                    Iterator<ArrayList<Callback>> it = mPackageModeWatchers.values().iterator();
-                    while (it.hasNext()) {
-                        ArrayList<Callback> cbs = it.next();
-                        cbs.remove(cb);
-                        if (cbs.size() <= 0) {
-                            it.remove();
-                        }
+                for (int i=mPackageModeWatchers.size()-1; i>=0; i--) {
+                    ArrayList<Callback> cbs = mPackageModeWatchers.valueAt(i);
+                    cbs.remove(cb);
+                    if (cbs.size() <= 0) {
+                        mPackageModeWatchers.removeAt(i);
                     }
                 }
             }
@@ -402,6 +445,18 @@
     }
 
     @Override
+    public IBinder getToken(IBinder clientToken) {
+        synchronized (this) {
+            ClientState cs = mClients.get(clientToken);
+            if (cs == null) {
+                cs = new ClientState(clientToken);
+                mClients.put(clientToken, cs);
+            }
+            return cs;
+        }
+    }
+
+    @Override
     public int checkOperation(int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
@@ -448,9 +503,10 @@
     }
 
     @Override
-    public int startOperation(int code, int uid, String packageName) {
+    public int startOperation(IBinder token, int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
+        ClientState client = (ClientState)token;
         synchronized (this) {
             Ops ops = getOpsLocked(uid, packageName, true);
             if (ops == null) {
@@ -475,32 +531,46 @@
                 op.duration = -1;
             }
             op.nesting++;
+            if (client.mStartedOps != null) {
+                client.mStartedOps.add(op);
+            }
             return AppOpsManager.MODE_ALLOWED;
         }
     }
 
     @Override
-    public void finishOperation(int code, int uid, String packageName) {
+    public void finishOperation(IBinder token, int code, int uid, String packageName) {
         verifyIncomingUid(uid);
         verifyIncomingOp(code);
+        ClientState client = (ClientState)token;
         synchronized (this) {
             Op op = getOpLocked(code, uid, packageName, true);
             if (op == null) {
                 return;
             }
-            if (op.nesting <= 1) {
-                if (op.nesting == 1) {
-                    op.duration = (int)(System.currentTimeMillis() - op.time);
-                    op.time += op.duration;
-                } else {
-                    Slog.w(TAG, "Finishing op nesting under-run: uid " + uid + " pkg " + packageName
-                        + " code " + code + " time=" + op.time + " duration=" + op.duration
-                        + " nesting=" + op.nesting);
+            if (client.mStartedOps != null) {
+                if (!client.mStartedOps.remove(op)) {
+                    throw new IllegalStateException("Operation not started: uid" + op.uid
+                            + " pkg=" + op.packageName + " op=" + op.op);
                 }
-                op.nesting = 0;
-            } else {
-                op.nesting--;
             }
+            finishOperationLocked(op);
+        }
+    }
+
+    void finishOperationLocked(Op op) {
+        if (op.nesting <= 1) {
+            if (op.nesting == 1) {
+                op.duration = (int)(System.currentTimeMillis() - op.time);
+                op.time += op.duration;
+            } else {
+                Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
+                        + op.packageName + " code " + op.op + " time=" + op.time
+                        + " duration=" + op.duration + " nesting=" + op.nesting);
+            }
+            op.nesting = 0;
+        } else {
+            op.nesting--;
         }
     }
 
@@ -601,7 +671,7 @@
             if (!edit) {
                 return null;
             }
-            op = new Op(code);
+            op = new Op(ops.uid, ops.packageName, code);
             ops.put(code, op);
         }
         if (edit) {
@@ -711,7 +781,7 @@
 
             String tagName = parser.getName();
             if (tagName.equals("op")) {
-                Op op = new Op(Integer.parseInt(parser.getAttributeValue(null, "n")));
+                Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n")));
                 String mode = parser.getAttributeValue(null, "m");
                 if (mode != null) {
                     op.mode = Integer.parseInt(mode);
@@ -831,6 +901,62 @@
         synchronized (this) {
             pw.println("Current AppOps Service state:");
             final long now = System.currentTimeMillis();
+            boolean needSep = false;
+            if (mOpModeWatchers.size() > 0) {
+                needSep = true;
+                pw.println("  Op mode watchers:");
+                for (int i=0; i<mOpModeWatchers.size(); i++) {
+                    pw.print("    Op "); pw.print(AppOpsManager.opToName(mOpModeWatchers.keyAt(i)));
+                    pw.println(":");
+                    ArrayList<Callback> callbacks = mOpModeWatchers.valueAt(i);
+                    for (int j=0; j<callbacks.size(); j++) {
+                        pw.print("      #"); pw.print(j); pw.print(": ");
+                        pw.println(callbacks.get(j));
+                    }
+                }
+            }
+            if (mPackageModeWatchers.size() > 0) {
+                needSep = true;
+                pw.println("  Package mode watchers:");
+                for (int i=0; i<mPackageModeWatchers.size(); i++) {
+                    pw.print("    Pkg "); pw.print(mPackageModeWatchers.keyAt(i));
+                    pw.println(":");
+                    ArrayList<Callback> callbacks = mPackageModeWatchers.valueAt(i);
+                    for (int j=0; j<callbacks.size(); j++) {
+                        pw.print("      #"); pw.print(j); pw.print(": ");
+                        pw.println(callbacks.get(j));
+                    }
+                }
+            }
+            if (mModeWatchers.size() > 0) {
+                needSep = true;
+                pw.println("  All mode watchers:");
+                for (int i=0; i<mModeWatchers.size(); i++) {
+                    pw.print("    "); pw.print(mModeWatchers.keyAt(i));
+                    pw.print(" -> "); pw.println(mModeWatchers.valueAt(i));
+                }
+            }
+            if (mClients.size() > 0) {
+                needSep = true;
+                pw.println("  Clients:");
+                for (int i=0; i<mClients.size(); i++) {
+                    pw.print("    "); pw.print(mClients.keyAt(i)); pw.println(":");
+                    ClientState cs = mClients.valueAt(i);
+                    pw.print("      "); pw.println(cs);
+                    if (cs.mStartedOps != null && cs.mStartedOps.size() > 0) {
+                        pw.println("      Started ops:");
+                        for (int j=0; j<cs.mStartedOps.size(); j++) {
+                            Op op = cs.mStartedOps.get(j);
+                            pw.print("        "); pw.print("uid="); pw.print(op.uid);
+                            pw.print(" pkg="); pw.print(op.packageName);
+                            pw.print(" op="); pw.println(AppOpsManager.opToName(op.op));
+                        }
+                    }
+                }
+            }
+            if (needSep) {
+                pw.println();
+            }
             for (int i=0; i<mUidOps.size(); i++) {
                 pw.print("  Uid "); UserHandle.formatUid(pw, mUidOps.keyAt(i)); pw.println(":");
                 HashMap<String, Ops> pkgOps = mUidOps.valueAt(i);
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/AttributeCache.java b/services/java/com/android/server/AttributeCache.java
index 81378dc..427dbc0 100644
--- a/services/java/com/android/server/AttributeCache.java
+++ b/services/java/com/android/server/AttributeCache.java
@@ -23,6 +23,7 @@
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
+import android.os.UserHandle;
 import android.util.SparseArray;
 
 import java.util.HashMap;
@@ -94,7 +95,7 @@
         }
     }
     
-    public Entry get(String packageName, int resId, int[] styleable) {
+    public Entry get(String packageName, int resId, int[] styleable, int userId) {
         synchronized (this) {
             Package pkg = mPackages.get(packageName);
             HashMap<int[], Entry> map = null;
@@ -110,7 +111,8 @@
             } else {
                 Context context;
                 try {
-                    context = mContext.createPackageContext(packageName, 0);
+                    context = mContext.createPackageContextAsUser(packageName, 0,
+                            new UserHandle(userId));
                     if (context == null) {
                         return null;
                     }
diff --git a/services/java/com/android/server/BatteryService.java b/services/java/com/android/server/BatteryService.java
index de58496..2d42cd61 100644
--- a/services/java/com/android/server/BatteryService.java
+++ b/services/java/com/android/server/BatteryService.java
@@ -372,9 +372,9 @@
                         mBatteryStatus, mBatteryHealth, mBatteryPresent ? 1 : 0,
                         mPlugType, mBatteryTechnology);
             }
-            if (mBatteryLevel != mLastBatteryLevel ||
-                    mBatteryVoltage != mLastBatteryVoltage ||
-                    mBatteryTemperature != mLastBatteryTemperature) {
+            if (mBatteryLevel != mLastBatteryLevel) {
+                // Don't do this just from voltage or temperature changes, that is
+                // too noisy.
                 EventLog.writeEvent(EventLogTags.BATTERY_LEVEL,
                         mBatteryLevel, mBatteryVoltage, mBatteryTemperature);
             }
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/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index f2e0f29..0608b6a 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -42,6 +42,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.content.pm.PackageManager;
+import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.database.ContentObserver;
 import android.net.CaptivePortalTracker;
@@ -96,7 +97,9 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.SparseIntArray;
+import android.util.Xml;
 
 import com.android.internal.R;
 import com.android.internal.net.LegacyVpnInfo;
@@ -106,6 +109,7 @@
 import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.PhoneConstants;
 import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.XmlUtils;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.connectivity.DataConnectionStats;
 import com.android.server.connectivity.Nat464Xlat;
@@ -116,9 +120,17 @@
 import com.google.android.collect.Lists;
 import com.google.android.collect.Sets;
 
+import com.android.internal.annotations.GuardedBy;
+
 import dalvik.system.DexClassLoader;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.lang.reflect.Constructor;
@@ -167,11 +179,11 @@
     private static final int MAX_HOSTROUTE_CYCLE_COUNT = 10;
 
     private Tethering mTethering;
-    private boolean mTetheringConfigValid = false;
 
     private KeyStore mKeyStore;
 
-    private Vpn mVpn;
+    @GuardedBy("mVpns")
+    private final SparseArray<Vpn> mVpns = new SparseArray<Vpn>();
     private VpnCallback mVpnCallback = new VpnCallback();
 
     private boolean mLockdownEnabled;
@@ -377,6 +389,9 @@
 
     TelephonyManager mTelephonyManager;
 
+    // We only want one checkMobileProvisioning after booting.
+    volatile boolean mFirstProvisioningCheckStarted = false;
+
     public ConnectivityService(Context context, INetworkManagementService netd,
             INetworkStatsService statsService, INetworkPolicyManager policyManager) {
         // Currently, omitting a NetworkFactory will create one internally
@@ -579,14 +594,13 @@
         }
 
         mTethering = new Tethering(mContext, mNetd, statsService, this, mHandler.getLooper());
-        mTetheringConfigValid = ((mTethering.getTetherableUsbRegexs().length != 0 ||
-                                  mTethering.getTetherableWifiRegexs().length != 0 ||
-                                  mTethering.getTetherableBluetoothRegexs().length != 0) &&
-                                 mTethering.getUpstreamIfaceTypes().length != 0);
 
-        mVpn = new Vpn(mContext, mVpnCallback, mNetd, this);
-        mVpn.startMonitoring(mContext, mTrackerHandler);
-
+        //set up the listener for user state for creating user VPNs
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_USER_STARTING);
+        intentFilter.addAction(Intent.ACTION_USER_STOPPING);
+        mContext.registerReceiverAsUser(
+                mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
         mClat = new Nat464Xlat(mContext, mNetd, this, mTrackerHandler);
 
         try {
@@ -1742,6 +1756,16 @@
                 "ConnectivityService");
     }
 
+    private void enforceMarkNetworkSocketPermission() {
+        //Media server special case
+        if (Binder.getCallingUid() == Process.MEDIA_UID) {
+            return;
+        }
+        mContext.enforceCallingOrSelfPermission(
+                android.Manifest.permission.MARK_NETWORK_SOCKET,
+                "ConnectivityService");
+    }
+
     /**
      * Handle a {@code DISCONNECTED} event. If this pertains to the non-active
      * network, we ignore it. If it is for the active network, we send out a
@@ -2313,7 +2337,11 @@
                             // Tell VPN the interface is down. It is a temporary
                             // but effective fix to make VPN aware of the change.
                             if ((resetMask & NetworkUtils.RESET_IPV4_ADDRESSES) != 0) {
-                                mVpn.interfaceStatusChanged(iface, false);
+                                synchronized(mVpns) {
+                                    for (int i = 0; i < mVpns.size(); i++) {
+                                        mVpns.valueAt(i).interfaceStatusChanged(iface, false);
+                                    }
+                                }
                             }
                         }
                         if (resetDns) {
@@ -2570,7 +2598,6 @@
 
         try {
             mNetd.setDnsServersForInterface(iface, NetworkUtils.makeStrings(dnses), domains);
-            mNetd.setDefaultInterfaceForDns(iface);
             for (InetAddress dns : dnses) {
                 ++last;
                 String key = "net.dns" + last;
@@ -2735,6 +2762,17 @@
                             state + "/" + info.getDetailedState());
                     }
 
+                    // After booting we'll check once for mobile provisioning
+                    // if we've provisioned by and connected.
+                    if (!mFirstProvisioningCheckStarted
+                            && (0 != Settings.Global.getInt(mContext.getContentResolver(),
+                                        Settings.Global.DEVICE_PROVISIONED, 0))
+                            && (state == NetworkInfo.State.CONNECTED)) {
+                        log("check provisioning after booting");
+                        mFirstProvisioningCheckStarted = true;
+                        checkMobileProvisioning(true, CheckMp.MAX_TIMEOUT_MS, null);
+                    }
+
                     EventLogTags.writeConnectivityStateChanged(
                             info.getType(), info.getSubtype(), info.getDetailedState().ordinal());
 
@@ -2984,7 +3022,10 @@
         int defaultVal = (SystemProperties.get("ro.tether.denied").equals("true") ? 0 : 1);
         boolean tetherEnabledInSettings = (Settings.Global.getInt(mContext.getContentResolver(),
                 Settings.Global.TETHER_SUPPORTED, defaultVal) != 0);
-        return tetherEnabledInSettings && mTetheringConfigValid;
+        return tetherEnabledInSettings && ((mTethering.getTetherableUsbRegexs().length != 0 ||
+                mTethering.getTetherableWifiRegexs().length != 0 ||
+                mTethering.getTetherableBluetoothRegexs().length != 0) &&
+                mTethering.getUpstreamIfaceTypes().length != 0);
     }
 
     // An API NetworkStateTrackers can call when they lose their network.
@@ -3305,8 +3346,12 @@
         throwIfLockdownEnabled();
         try {
             int type = mActiveDefaultNetwork;
+            int user = UserHandle.getUserId(Binder.getCallingUid());
             if (ConnectivityManager.isNetworkTypeValid(type) && mNetTrackers[type] != null) {
-                mVpn.protect(socket, mNetTrackers[type].getLinkProperties().getInterfaceName());
+                synchronized(mVpns) {
+                    mVpns.get(user).protect(socket,
+                            mNetTrackers[type].getLinkProperties().getInterfaceName());
+                }
                 return true;
             }
         } catch (Exception e) {
@@ -3330,7 +3375,27 @@
     @Override
     public boolean prepareVpn(String oldPackage, String newPackage) {
         throwIfLockdownEnabled();
-        return mVpn.prepare(oldPackage, newPackage);
+        int user = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized(mVpns) {
+            return mVpns.get(user).prepare(oldPackage, newPackage);
+        }
+    }
+
+    @Override
+    public void markSocketAsUser(ParcelFileDescriptor socket, int uid) {
+        enforceMarkNetworkSocketPermission();
+        final long token = Binder.clearCallingIdentity();
+        try {
+            int mark = mNetd.getMarkForUid(uid);
+            // Clear the mark on the socket if no mark is needed to prevent socket reuse issues
+            if (mark == -1) {
+                mark = 0;
+            }
+            NetworkUtils.markSocket(socket.getFd(), mark);
+        } catch (RemoteException e) {
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
     }
 
     /**
@@ -3343,7 +3408,10 @@
     @Override
     public ParcelFileDescriptor establishVpn(VpnConfig config) {
         throwIfLockdownEnabled();
-        return mVpn.establish(config);
+        int user = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized(mVpns) {
+            return mVpns.get(user).establish(config);
+        }
     }
 
     /**
@@ -3357,7 +3425,10 @@
         if (egress == null) {
             throw new IllegalStateException("Missing active network connection");
         }
-        mVpn.startLegacyVpn(profile, mKeyStore, egress);
+        int user = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized(mVpns) {
+            mVpns.get(user).startLegacyVpn(profile, mKeyStore, egress);
+        }
     }
 
     /**
@@ -3369,7 +3440,10 @@
     @Override
     public LegacyVpnInfo getLegacyVpnInfo() {
         throwIfLockdownEnabled();
-        return mVpn.getLegacyVpnInfo();
+        int user = UserHandle.getUserId(Binder.getCallingUid());
+        synchronized(mVpns) {
+            return mVpns.get(user).getLegacyVpnInfo();
+        }
     }
 
     /**
@@ -3390,7 +3464,7 @@
             mHandler.obtainMessage(EVENT_VPN_STATE_CHANGED, info).sendToTarget();
         }
 
-        public void override(List<String> dnsServers, List<String> searchDomains) {
+        public void override(String iface, List<String> dnsServers, List<String> searchDomains) {
             if (dnsServers == null) {
                 restore();
                 return;
@@ -3422,7 +3496,7 @@
 
             // Apply DNS changes.
             synchronized (mDnsLock) {
-                updateDnsLocked("VPN", "VPN", addresses, domains);
+                updateDnsLocked("VPN", iface, addresses, domains);
                 mDnsOverridden = true;
             }
 
@@ -3451,6 +3525,67 @@
                 }
             }
         }
+
+        public void protect(ParcelFileDescriptor socket) {
+            try {
+                final int mark = mNetd.getMarkForProtect();
+                NetworkUtils.markSocket(socket.getFd(), mark);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void setRoutes(String interfaze, List<RouteInfo> routes) {
+            for (RouteInfo route : routes) {
+                try {
+                    mNetd.setMarkedForwardingRoute(interfaze, route);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+
+        public void setMarkedForwarding(String interfaze) {
+            try {
+                mNetd.setMarkedForwarding(interfaze);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void clearMarkedForwarding(String interfaze) {
+            try {
+                mNetd.clearMarkedForwarding(interfaze);
+            } catch (RemoteException e) {
+            }
+        }
+
+        public void addUserForwarding(String interfaze, int uid) {
+            int uidStart = uid * UserHandle.PER_USER_RANGE;
+            int uidEnd = uidStart + UserHandle.PER_USER_RANGE - 1;
+            addUidForwarding(interfaze, uidStart, uidEnd);
+        }
+
+        public void clearUserForwarding(String interfaze, int uid) {
+            int uidStart = uid * UserHandle.PER_USER_RANGE;
+            int uidEnd = uidStart + UserHandle.PER_USER_RANGE - 1;
+            clearUidForwarding(interfaze, uidStart, uidEnd);
+        }
+
+        public void addUidForwarding(String interfaze, int uidStart, int uidEnd) {
+            try {
+                mNetd.setUidRangeRoute(interfaze,uidStart, uidEnd);
+                mNetd.setDnsInterfaceForUidRange(interfaze, uidStart, uidEnd);
+            } catch (RemoteException e) {
+            }
+
+        }
+
+        public void clearUidForwarding(String interfaze, int uidStart, int uidEnd) {
+            try {
+                mNetd.clearUidRangeRoute(interfaze, uidStart, uidEnd);
+                mNetd.clearDnsInterfaceForUidRange(uidStart, uidEnd);
+            } catch (RemoteException e) {
+            }
+
+        }
     }
 
     @Override
@@ -3471,7 +3606,11 @@
             final String profileName = new String(mKeyStore.get(Credentials.LOCKDOWN_VPN));
             final VpnProfile profile = VpnProfile.decode(
                     profileName, mKeyStore.get(Credentials.VPN + profileName));
-            setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpn, profile));
+            int user = UserHandle.getUserId(Binder.getCallingUid());
+            synchronized(mVpns) {
+                setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, mVpns.get(user),
+                            profile));
+            }
         } else {
             setLockdownTracker(null);
         }
@@ -3552,13 +3691,15 @@
     }
 
     @Override
-    public int checkMobileProvisioning(boolean sendNotification, int suggestedTimeOutMs,
+    public int checkMobileProvisioning(final boolean sendNotification, int suggestedTimeOutMs,
             final ResultReceiver resultReceiver) {
         log("checkMobileProvisioning: E sendNotification=" + sendNotification
                 + " suggestedTimeOutMs=" + suggestedTimeOutMs
                 + " resultReceiver=" + resultReceiver);
         enforceChangePermission();
 
+        mFirstProvisioningCheckStarted = true;
+
         int timeOutMs = suggestedTimeOutMs;
         if (suggestedTimeOutMs > CheckMp.MAX_TIMEOUT_MS) {
             timeOutMs = CheckMp.MAX_TIMEOUT_MS;
@@ -3585,6 +3726,10 @@
                         log("CheckMp.onComplete: send result");
                         resultReceiver.send(result, null);
                     }
+                    if (!sendNotification) {
+                        log("CheckMp.onComplete: done, not sending notification");
+                        return;
+                    }
                     NetworkInfo ni =
                             mNetTrackers[ConnectivityManager.TYPE_MOBILE_HIPRI].getNetworkInfo();
                     switch(result) {
@@ -3595,10 +3740,9 @@
                         }
                         case ConnectivityManager.CMP_RESULT_CODE_REDIRECTED: {
                             log("CheckMp.onComplete: warm sim");
-                            String url = getProvisioningUrl();
+                            String url = getMobileProvisioningUrl();
                             if (TextUtils.isEmpty(url)) {
-                                url = mContext.getResources()
-                                        .getString(R.string.mobile_redirected_provisioning_url);
+                                url = getMobileRedirectedProvisioningUrl();
                             }
                             if (TextUtils.isEmpty(url) == false) {
                                 log("CheckMp.onComplete: warm sim (redirected), url=" + url);
@@ -3610,7 +3754,7 @@
                         }
                         case ConnectivityManager.CMP_RESULT_CODE_NO_DNS:
                         case ConnectivityManager.CMP_RESULT_CODE_NO_TCP_CONNECTION: {
-                            String url = getProvisioningUrl();
+                            String url = getMobileProvisioningUrl();
                             if (TextUtils.isEmpty(url) == false) {
                                 log("CheckMp.onComplete: warm sim (no dns/tcp), url=" + url);
                                 setNotificationVisible(true, ni, url);
@@ -3984,19 +4128,165 @@
         log("setNotificationVisible: X visible=" + visible + " ni=" + networkInfo + " url=" + url);
     }
 
-    private String getProvisioningUrl() {
-        String url = mContext.getResources().getString(R.string.mobile_provisioning_url);
-        log("getProvisioningUrl: resource url=" + url);
+    /** Location to an updatable file listing carrier provisioning urls.
+     *  An example:
+     *
+     * <?xml version="1.0" encoding="utf-8"?>
+     *  <provisioningUrls>
+     *   <provisioningUrl mcc="310" mnc="4">http://myserver.com/foo?mdn=%3$s&amp;iccid=%1$s&amp;imei=%2$s</provisioningUrl>
+     *   <redirectedUrl mcc="310" mnc="4">http://www.google.com</redirectedUrl>
+     *  </provisioningUrls>
+     */
+    private static final String PROVISIONING_URL_PATH =
+            "/data/misc/radio/provisioning_urls.xml";
+    private final File mProvisioningUrlFile = new File(PROVISIONING_URL_PATH);
 
-        // populate the iccid and imei in the provisioning url.
+    /** XML tag for root element. */
+    private static final String TAG_PROVISIONING_URLS = "provisioningUrls";
+    /** XML tag for individual url */
+    private static final String TAG_PROVISIONING_URL = "provisioningUrl";
+    /** XML tag for redirected url */
+    private static final String TAG_REDIRECTED_URL = "redirectedUrl";
+    /** XML attribute for mcc */
+    private static final String ATTR_MCC = "mcc";
+    /** XML attribute for mnc */
+    private static final String ATTR_MNC = "mnc";
+
+    private static final int REDIRECTED_PROVISIONING = 1;
+    private static final int PROVISIONING = 2;
+
+    private String getProvisioningUrlBaseFromFile(int type) {
+        FileReader fileReader = null;
+        XmlPullParser parser = null;
+        Configuration config = mContext.getResources().getConfiguration();
+        String tagType;
+
+        switch (type) {
+            case PROVISIONING:
+                tagType = TAG_PROVISIONING_URL;
+                break;
+            case REDIRECTED_PROVISIONING:
+                tagType = TAG_REDIRECTED_URL;
+                break;
+            default:
+                throw new RuntimeException("getProvisioningUrlBaseFromFile: Unexpected parameter " +
+                        type);
+        }
+
+        try {
+            fileReader = new FileReader(mProvisioningUrlFile);
+            parser = Xml.newPullParser();
+            parser.setInput(fileReader);
+            XmlUtils.beginDocument(parser, TAG_PROVISIONING_URLS);
+
+            while (true) {
+                XmlUtils.nextElement(parser);
+
+                String element = parser.getName();
+                if (element == null) break;
+
+                if (element.equals(tagType)) {
+                    String mcc = parser.getAttributeValue(null, ATTR_MCC);
+                    try {
+                        if (mcc != null && Integer.parseInt(mcc) == config.mcc) {
+                            String mnc = parser.getAttributeValue(null, ATTR_MNC);
+                            if (mnc != null && Integer.parseInt(mnc) == config.mnc) {
+                                parser.next();
+                                if (parser.getEventType() == XmlPullParser.TEXT) {
+                                    return parser.getText();
+                                }
+                            }
+                        }
+                    } catch (NumberFormatException e) {
+                        loge("NumberFormatException in getProvisioningUrlBaseFromFile: " + e);
+                    }
+                }
+            }
+            return null;
+        } catch (FileNotFoundException e) {
+            loge("Carrier Provisioning Urls file not found");
+        } catch (XmlPullParserException e) {
+            loge("Xml parser exception reading Carrier Provisioning Urls file: " + e);
+        } catch (IOException e) {
+            loge("I/O exception reading Carrier Provisioning Urls file: " + e);
+        } finally {
+            if (fileReader != null) {
+                try {
+                    fileReader.close();
+                } catch (IOException e) {}
+            }
+        }
+        return null;
+    }
+
+    private String getMobileRedirectedProvisioningUrl() {
+        String url = getProvisioningUrlBaseFromFile(REDIRECTED_PROVISIONING);
+        if (TextUtils.isEmpty(url)) {
+            url = mContext.getResources().getString(R.string.mobile_redirected_provisioning_url);
+        }
+        return url;
+    }
+
+    public String getMobileProvisioningUrl() {
+        enforceConnectivityInternalPermission();
+        String url = getProvisioningUrlBaseFromFile(PROVISIONING);
+        if (TextUtils.isEmpty(url)) {
+            url = mContext.getResources().getString(R.string.mobile_provisioning_url);
+            log("getProvisioningUrl: mobile_provisioining_url from resource =" + url);
+        } else {
+            log("getProvisioningUrl: mobile_provisioning_url from File =" + url);
+        }
+        // populate the iccid, imei and phone number in the provisioning url.
         if (!TextUtils.isEmpty(url)) {
+            String phoneNumber = mTelephonyManager.getLine1Number();
+            if (TextUtils.isEmpty(phoneNumber)) {
+                phoneNumber = "0000000000";
+            }
             url = String.format(url,
                     mTelephonyManager.getSimSerialNumber() /* ICCID */,
                     mTelephonyManager.getDeviceId() /* IMEI */,
-                    mTelephonyManager.getLine1Number() /* Phone numer */);
+                    phoneNumber /* Phone numer */);
         }
 
-        log("getProvisioningUrl: url=" + url);
         return url;
     }
+
+    private void onUserStart(int userId) {
+        synchronized(mVpns) {
+            Vpn userVpn = mVpns.get(userId);
+            if (userVpn != null) {
+                loge("Starting user already has a VPN");
+                return;
+            }
+            userVpn = new Vpn(mContext, mVpnCallback, mNetd, this, userId);
+            mVpns.put(userId, userVpn);
+            userVpn.startMonitoring(mContext, mTrackerHandler);
+        }
+    }
+
+    private void onUserStop(int userId) {
+        synchronized(mVpns) {
+            Vpn userVpn = mVpns.get(userId);
+            if (userVpn == null) {
+                loge("Stopping user has no VPN");
+                return;
+            }
+            mVpns.delete(userId);
+        }
+    }
+
+    private BroadcastReceiver mUserIntentReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            final String action = intent.getAction();
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+            if (userId == UserHandle.USER_NULL) return;
+
+            if (Intent.ACTION_USER_STARTING.equals(action)) {
+                onUserStart(userId);
+            } else if (Intent.ACTION_USER_STOPPING.equals(action)) {
+                onUserStop(userId);
+            }
+        }
+    };
 }
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 231cfe1..bde9e1c 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -47,7 +47,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.Handler;
-import android.os.HandlerThread;
 import android.os.IBinder;
 import android.os.Looper;
 import android.os.Message;
@@ -199,7 +198,7 @@
         // most startup is deferred until systemReady()
     }
 
-    public void systemReady() {
+    public void systemRunning() {
         synchronized (mLock) {
             if (D) Log.d(TAG, "systemReady()");
 
@@ -222,6 +221,9 @@
             AppOpsManager.Callback callback = new AppOpsManager.Callback() {
                 public void opChanged(int op, String packageName) {
                     synchronized (mLock) {
+                        for (Receiver receiver : mReceivers.values()) {
+                            receiver.updateMonitoring(true);
+                        }
                         applyAllProviderRequirementsLocked();
                     }
                 }
@@ -460,6 +462,7 @@
 
         final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
 
+        boolean mOpMonitoring;
         int mPendingBroadcasts;
         PowerManager.WakeLock mWakeLock;
 
@@ -477,6 +480,8 @@
             mPid = pid;
             mPackageName = packageName;
 
+            updateMonitoring(true);
+
             // construct/configure wakelock
             mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
             mWakeLock.setWorkSource(new WorkSource(mUid, mPackageName));
@@ -512,6 +517,21 @@
             return s.toString();
         }
 
+        public void updateMonitoring(boolean allow) {
+            if (!mOpMonitoring) {
+                if (allow) {
+                    mOpMonitoring = mAppOps.startOpNoThrow(AppOpsManager.OP_MONITOR_LOCATION,
+                            mUid, mPackageName) == AppOpsManager.MODE_ALLOWED;
+                }
+            } else {
+                if (!allow || mAppOps.checkOpNoThrow(AppOpsManager.OP_MONITOR_LOCATION,
+                        mUid, mPackageName) != AppOpsManager.MODE_ALLOWED) {
+                    mAppOps.finishOp(AppOpsManager.OP_MONITOR_LOCATION, mUid, mPackageName);
+                    mOpMonitoring = false;
+                }
+            }
+        }
+
         public boolean isListener() {
             return mListener != null;
         }
@@ -1366,6 +1386,8 @@
             }
         }
 
+        receiver.updateMonitoring(false);
+
         // Record which providers were associated with this listener
         HashSet<String> providers = new HashSet<String>();
         HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 6f740cd0..c3a43bb 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -32,6 +32,7 @@
 import static com.android.server.NetworkManagementService.NetdResponseCode.TetherStatusResult;
 import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsResult;
 import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
+import static com.android.server.NetworkManagementService.NetdResponseCode.GetMarkResult;
 import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
 
 import android.content.Context;
@@ -127,6 +128,7 @@
         public static final int TetheringStatsResult      = 221;
         public static final int DnsProxyQueryResult       = 222;
         public static final int ClatdStatusResult         = 223;
+        public static final int GetMarkResult             = 225;
 
         public static final int InterfaceChange           = 600;
         public static final int BandwidthControl          = 601;
@@ -1382,7 +1384,7 @@
     public void setUidRangeRoute(String iface, int uid_start, int uid_end) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("interface", "route",
+            mConnector.execute("interface", "fwmark",
                     "uid", "add", iface, uid_start, uid_end);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
@@ -1393,7 +1395,7 @@
     public void clearUidRangeRoute(String iface, int uid_start, int uid_end) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("interface", "route",
+            mConnector.execute("interface", "fwmark",
                     "uid", "remove", iface, uid_start, uid_end);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
@@ -1404,7 +1406,7 @@
     public void setMarkedForwarding(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("interface", "route", "fwmark", "add", iface);
+            mConnector.execute("interface", "fwmark", "rule", "add", iface);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
@@ -1414,7 +1416,57 @@
     public void clearMarkedForwarding(String iface) {
         mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
         try {
-            mConnector.execute("interface", "route", "fwmark", "remove", iface);
+            mConnector.execute("interface", "fwmark", "rule", "remove", iface);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
+    public int getMarkForUid(int uid) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        final NativeDaemonEvent event;
+        try {
+            event = mConnector.execute("interface", "fwmark", "get", "mark", uid);
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+        event.checkCode(GetMarkResult);
+        return Integer.parseInt(event.getMessage());
+    }
+
+    @Override
+    public int getMarkForProtect() {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        final NativeDaemonEvent event;
+        try {
+            event = mConnector.execute("interface", "fwmark", "get", "protect");
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+        event.checkCode(GetMarkResult);
+        return Integer.parseInt(event.getMessage());
+    }
+
+    @Override
+    public void setMarkedForwardingRoute(String iface, RouteInfo route) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        try {
+            LinkAddress dest = route.getDestination();
+            mConnector.execute("interface", "fwmark", "route", "add", iface,
+                    dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength());
+        } catch (NativeDaemonConnectorException e) {
+            throw e.rethrowAsParcelableException();
+        }
+    }
+
+    @Override
+    public void clearMarkedForwardingRoute(String iface, RouteInfo route) {
+        mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
+        try {
+            LinkAddress dest = route.getDestination();
+            mConnector.execute("interface", "fwmark", "route", "remove", iface,
+                    dest.getAddress().getHostAddress(), dest.getNetworkPrefixLength());
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }
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 acfc096..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);
                 }
             }
         });
@@ -1085,11 +1094,9 @@
     private static final long EARLIEST_SUPPORTED_TIME = 86400 * 1000;
 
     /**
-     * This method is called from Zygote to initialize the system. This will cause the native
-     * services (SurfaceFlinger, AudioFlinger, etc..) to be started. After that it will call back
-     * up into init2() to start the Android services.
+     * Called to initialize native system services.
      */
-    native public static void init1(String[] args);
+    private static native void nativeInit();
 
     public static void main(String[] args) {
         if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {
@@ -1123,12 +1130,12 @@
         Environment.setUserRequired(true);
 
         System.loadLibrary("android_servers");
-        init1(args);
-    }
 
-    public static final void init2() {
         Slog.i(TAG, "Entered the Android system server!");
 
+        // Initialize native services.
+        nativeInit();
+
         // This used to be its own separate thread, but now it is
         // just the loop we run on the main thread.
         ServerThread thr = new ServerThread();
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/TwilightService.java b/services/java/com/android/server/TwilightService.java
index 154de1c..0356faa 100644
--- a/services/java/com/android/server/TwilightService.java
+++ b/services/java/com/android/server/TwilightService.java
@@ -520,7 +520,7 @@
             Intent updateIntent = new Intent(ACTION_UPDATE_TWILIGHT_STATE);
             PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, updateIntent, 0);
             mAlarmManager.cancel(pendingIntent);
-            mAlarmManager.set(AlarmManager.RTC_WAKEUP, nextUpdate, pendingIntent);
+            mAlarmManager.setExact(AlarmManager.RTC, nextUpdate, pendingIntent);
         }
     };
 
diff --git a/services/java/com/android/server/VibratorService.java b/services/java/com/android/server/VibratorService.java
index 9b5f8f6..28eb948 100644
--- a/services/java/com/android/server/VibratorService.java
+++ b/services/java/com/android/server/VibratorService.java
@@ -342,7 +342,8 @@
     // Lock held on mVibrations
     private void startVibrationLocked(final Vibration vib) {
         try {
-            int mode = mAppOpsService.startOperation(AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
+            int mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
+                    AppOpsManager.OP_VIBRATE, vib.mUid, vib.mPackageName);
             if (mode != AppOpsManager.MODE_ALLOWED) {
                 if (mode == AppOpsManager.MODE_ERRORED) {
                     Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
@@ -366,7 +367,8 @@
     private void reportFinishVibrationLocked() {
         if (mCurrentVibration != null) {
             try {
-                mAppOpsService.finishOperation(AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
+                mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
+                        AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
                         mCurrentVibration.mPackageName);
             } catch (RemoteException e) {
             }
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/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index fd6b467..83e69d6f 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2820,7 +2820,8 @@
 
         public int resolveCallingUserIdEnforcingPermissionsLocked(int userId) {
             final int callingUid = Binder.getCallingUid();
-            if (callingUid == Process.SYSTEM_UID
+            if (callingUid == 0
+                    || callingUid == Process.SYSTEM_UID
                     || callingUid == Process.SHELL_UID) {
                 return mCurrentUserId;
             }
diff --git a/services/java/com/android/server/am/ActiveServices.java b/services/java/com/android/server/am/ActiveServices.java
index 3c10480..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;
@@ -852,7 +853,9 @@
         if ((!i.requested || rebind) && i.apps.size() > 0) {
             try {
                 bumpServiceExecutingLocked(r, "bind");
-                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);
+                r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
+                r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,
+                        r.app.repProcState);
                 if (!rebind) {
                     i.requested = true;
                 }
@@ -1118,14 +1121,19 @@
 
         boolean created = false;
         try {
+            String nameTerm;
+            int lastPeriod = r.shortName.lastIndexOf('.');
+            nameTerm = lastPeriod >= 0 ? r.shortName.substring(lastPeriod) : r.shortName;
             EventLogTags.writeAmCreateService(
-                    r.userId, System.identityHashCode(r), r.shortName, r.app.pid);
+                    r.userId, System.identityHashCode(r), nameTerm, r.app.uid, r.app.pid);
             synchronized (r.stats.getBatteryStats()) {
                 r.stats.startLaunchedLocked();
             }
             mAm.ensurePackageDexOpt(r.serviceInfo.packageName);
+            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);
             app.thread.scheduleCreateService(r, r.serviceInfo,
-                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));
+                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
+                    app.repProcState);
             r.postNotification();
             created = true;
         } finally {
@@ -1663,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.
@@ -1873,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 39ce0c6..ef84254 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -30,6 +30,7 @@
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.ResolverActivity;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.BatteryStatsImpl;
 import com.android.internal.os.ProcessStats;
@@ -479,38 +480,31 @@
         int pid;
         IBinder token;
     }
-    final SparseArray<ForegroundToken> mForegroundProcesses
-            = new SparseArray<ForegroundToken>();
+    final SparseArray<ForegroundToken> mForegroundProcesses = new SparseArray<ForegroundToken>();
 
     /**
      * List of records for processes that someone had tried to start before the
      * system was ready.  We don't start them at that point, but ensure they
      * are started by the time booting is complete.
      */
-    final ArrayList<ProcessRecord> mProcessesOnHold
-            = new ArrayList<ProcessRecord>();
+    final ArrayList<ProcessRecord> mProcessesOnHold = new ArrayList<ProcessRecord>();
 
     /**
      * List of persistent applications that are in the process
      * of being started.
      */
-    final ArrayList<ProcessRecord> mPersistentStartingProcesses
-            = new ArrayList<ProcessRecord>();
+    final ArrayList<ProcessRecord> mPersistentStartingProcesses = new ArrayList<ProcessRecord>();
 
     /**
      * Processes that are being forcibly torn down.
      */
-    final ArrayList<ProcessRecord> mRemovedProcesses
-            = new ArrayList<ProcessRecord>();
+    final ArrayList<ProcessRecord> mRemovedProcesses = new ArrayList<ProcessRecord>();
 
     /**
      * List of running applications, sorted by recent usage.
      * The first entry in the list is the least recently used.
-     * It contains ApplicationRecord objects.  This list does NOT include
-     * any persistent application records (since we never want to exit them).
      */
-    final ArrayList<ProcessRecord> mLruProcesses
-            = new ArrayList<ProcessRecord>();
+    final ArrayList<ProcessRecord> mLruProcesses = new ArrayList<ProcessRecord>();
 
     /**
      * List of processes that should gc as soon as things are idle.
@@ -1000,7 +994,6 @@
     static final int USER_SWITCH_TIMEOUT_MSG = 36;
     static final int IMMERSIVE_MODE_LOCK_MSG = 37;
     static final int PERSIST_URI_GRANTS = 38;
-    static final int SET_FOCUSED_STACK = 39;
 
     static final int FIRST_ACTIVITY_STACK_MSG = 100;
     static final int FIRST_BROADCAST_QUEUE_MSG = 200;
@@ -1493,18 +1486,6 @@
                 writeGrantedUriPermissions();
                 break;
             }
-            case SET_FOCUSED_STACK: {
-                synchronized (ActivityManagerService.this) {
-                    ActivityStack stack = mStackSupervisor.getStack(msg.arg1);
-                    if (stack != null) {
-                        ActivityRecord r = stack.topRunningActivityLocked(null);
-                        if (r != null) {
-                            setFocusedActivityLocked(r);
-                        }
-                    }
-                }
-                break;
-            }
             }
         }
     };
@@ -1518,6 +1499,7 @@
             case COLLECT_PSS_BG_MSG: {
                 int i=0;
                 long start = SystemClock.uptimeMillis();
+                long[] tmp = new long[1];
                 do {
                     ProcessRecord proc;
                     int oomAdj;
@@ -1541,10 +1523,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);
                             }
                         }
                     }
@@ -2068,7 +2050,7 @@
 
     final void setFocusedActivityLocked(ActivityRecord r) {
         if (mFocusedActivity != r) {
-            if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedActivitiyLocked: r=" + r);
+            if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedActivityLocked: r=" + r);
             mFocusedActivity = r;
             mStackSupervisor.setFocusedStack(r);
             if (r != null) {
@@ -2081,7 +2063,26 @@
     @Override
     public void setFocusedStack(int stackId) {
         if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: stackId=" + stackId);
-        mHandler.obtainMessage(SET_FOCUSED_STACK, stackId, 0).sendToTarget();
+        synchronized (ActivityManagerService.this) {
+            ActivityStack stack = mStackSupervisor.getStack(stackId);
+            if (stack != null) {
+                ActivityRecord r = stack.topRunningActivityLocked(null);
+                if (r != null) {
+                    setFocusedActivityLocked(r);
+                }
+            }
+        }
+    }
+
+    @Override
+    public void notifyActivityDrawn(IBinder token) {
+        if (DEBUG_VISBILITY) Slog.d(TAG, "notifyActivityDrawn: token=" + token);
+        synchronized (this) {
+            ActivityRecord r= mStackSupervisor.isInAnyStackLocked(token);
+            if (r != null) {
+                r.task.stack.notifyActivityDrawnLocked(r);
+            }
+        }
     }
 
     final void applyUpdateLockStateLocked(ActivityRecord r) {
@@ -2151,13 +2152,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--) {
@@ -2487,6 +2487,20 @@
         }
     }
 
+    String getHomePackageName() {
+        Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
+        intent.setComponent(mTopComponent);
+        intent.addCategory(Intent.CATEGORY_HOME);
+        ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, mCurrentUserId);
+        if (aInfo != null) {
+            final String homePackageName = aInfo.applicationInfo.packageName;
+            if (!ResolverActivity.class.getName().equals(homePackageName)) {
+                return homePackageName;
+            }
+        }
+        return null;
+    }
+
     boolean startHomeActivityLocked(int userId) {
         if (mHeadless) {
             // Added because none of the other calls to ensureBootCompleted seem to fire
@@ -4004,7 +4018,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);
                     }
                 }
             }
@@ -4025,12 +4040,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);
                     }
                 }
             }
@@ -8048,7 +8064,7 @@
     }
 
     @Override
-    public void convertToOpaque(IBinder token) {
+    public void convertFromTranslucent(IBinder token) {
         final long origId = Binder.clearCallingIdentity();
         try {
             synchronized (this) {
@@ -8056,8 +8072,28 @@
                 if (r == null) {
                     return;
                 }
-                if (r.convertToOpaque()) {
-                    mWindowManager.setAppFullscreen(token);
+                if (r.changeWindowTranslucency(true)) {
+                    mWindowManager.setAppFullscreen(token, true);
+                    mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
+                }
+            }
+        } finally {
+            Binder.restoreCallingIdentity(origId);
+        }
+    }
+
+    @Override
+    public void convertToTranslucent(IBinder token) {
+        final long origId = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final ActivityRecord r = ActivityRecord.isInStackLocked(token);
+                if (r == null) {
+                    return;
+                }
+                if (r.changeWindowTranslucency(false)) {
+                    r.task.stack.convertToTranslucent(r);
+                    mWindowManager.setAppFullscreen(token, false);
                     mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
                 }
             }
@@ -8776,14 +8812,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
@@ -10243,8 +10276,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);
@@ -10746,10 +10779,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) {
@@ -10763,30 +10796,96 @@
             } else {
                 oomAdj = Integer.toString(r.setAdj);
             }
-            String schedGroup;
+            char schedGroup;
             switch (r.setSchedGroup) {
                 case Process.THREAD_GROUP_BG_NONINTERACTIVE:
-                    schedGroup = "B";
+                    schedGroup = 'B';
                     break;
                 case Process.THREAD_GROUP_DEFAULT:
-                    schedGroup = "F";
+                    schedGroup = 'F';
                     break;
                 default:
-                    schedGroup = Integer.toString(r.setSchedGroup);
+                    schedGroup = '?';
                     break;
             }
-            String foreground;
+            char foreground;
             if (r.foregroundActivities) {
-                foreground = "A";
+                foreground = 'A';
             } else if (r.foregroundServices) {
-                foreground = "S";
+                foreground = 'S';
             } else {
-                foreground = " ";
+                foreground = ' ';
             }
-            pw.println(String.format("%s%s #%2d: adj=%s/%s%s trm=%2d %s (%s)",
-                    prefix, (r.persistent ? persistentLabel : normalLabel),
-                    (origList.size()-1)-list.get(i).second, oomAdj, schedGroup,
-                    foreground, r.trimMemoryLevel, r.toShortString(), r.adjType));
+            String procState;
+            switch (r.curProcState) {
+                case ActivityManager.PROCESS_STATE_PERSISTENT:
+                    procState = "P ";
+                    break;
+                case ActivityManager.PROCESS_STATE_PERSISTENT_UI:
+                    procState = "PU";
+                    break;
+                case ActivityManager.PROCESS_STATE_TOP:
+                    procState = "T ";
+                    break;
+                case ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND:
+                    procState = "IF";
+                    break;
+                case ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND:
+                    procState = "IB";
+                    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 = "HO";
+                    break;
+                case ActivityManager.PROCESS_STATE_LAST_ACTIVITY:
+                    procState = "LA";
+                    break;
+                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 = "??";
+                    break;
+            }
+            pw.print(prefix);
+            pw.print(r.persistent ? persistentLabel : normalLabel);
+            pw.print(" #");
+            int num = (origList.size()-1)-list.get(i).second;
+            if (num < 10) pw.print(' ');
+            pw.print(num);
+            pw.print(": ");
+            pw.print(oomAdj);
+            pw.print(' ');
+            pw.print(schedGroup);
+            pw.print('/');
+            pw.print(foreground);
+            pw.print('/');
+            pw.print(procState);
+            pw.print(" trm:");
+            if (r.trimMemoryLevel < 10) pw.print(' ');
+            pw.print(r.trimMemoryLevel);
+            pw.print(' ');
+            pw.print(r.toShortString());
+            pw.print(" (");
+            pw.print(r.adjType);
+            pw.println(')');
             if (r.adjSource != null || r.adjTarget != null) {
                 pw.print(prefix);
                 pw.print("    ");
@@ -10812,9 +10911,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);
@@ -10963,23 +11059,37 @@
     }
 
     final static class MemItem {
+        final boolean isProc;
         final String label;
         final String shortLabel;
         final long pss;
         final int id;
+        final boolean hasActivities;
         ArrayList<MemItem> subitems;
 
-        public MemItem(String _label, String _shortLabel, long _pss, int _id) {
+        public MemItem(String _label, String _shortLabel, long _pss, int _id,
+                boolean _hasActivities) {
+            isProc = true;
             label = _label;
             shortLabel = _shortLabel;
             pss = _pss;
             id = _id;
+            hasActivities = _hasActivities;
+        }
+
+        public MemItem(String _label, String _shortLabel, long _pss, int _id) {
+            isProc = false;
+            label = _label;
+            shortLabel = _shortLabel;
+            pss = _pss;
+            id = _id;
+            hasActivities = false;
         }
     }
 
-    static final void dumpMemItems(PrintWriter pw, String prefix, ArrayList<MemItem> items,
-            boolean sort) {
-        if (sort) {
+    static final void dumpMemItems(PrintWriter pw, String prefix, String tag,
+            ArrayList<MemItem> items, boolean sort, boolean isCompact) {
+        if (sort && !isCompact) {
             Collections.sort(items, new Comparator<MemItem>() {
                 @Override
                 public int compare(MemItem lhs, MemItem rhs) {
@@ -10995,9 +11105,19 @@
 
         for (int i=0; i<items.size(); i++) {
             MemItem mi = items.get(i);
-            pw.print(prefix); pw.printf("%7d kB: ", mi.pss); pw.println(mi.label);
+            if (!isCompact) {
+                pw.print(prefix); pw.printf("%7d kB: ", mi.pss); pw.println(mi.label);
+            } else if (mi.isProc) {
+                pw.print("proc,"); pw.print(tag); pw.print(","); pw.print(mi.shortLabel);
+                pw.print(","); pw.print(mi.id); pw.print(","); pw.print(mi.pss);
+                pw.println(mi.hasActivities ? ",a" : ",e");
+            } else {
+                pw.print(tag); pw.print(","); pw.print(mi.shortLabel); pw.print(",");
+                pw.println(mi.pss);
+            }
             if (mi.subitems != null) {
-                dumpMemItems(pw, prefix + "           ", mi.subitems, true);
+                dumpMemItems(pw, prefix + "           ", mi.shortLabel, mi.subitems,
+                        true, isCompact);
             }
         }
     }
@@ -11032,15 +11152,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"
     };
 
     final void dumpApplicationMemoryUsage(FileDescriptor fd,
@@ -11049,6 +11178,7 @@
         boolean dumpDetails = false;
         boolean dumpDalvik = false;
         boolean oomOnly = false;
+        boolean isCompact = false;
         
         int opti = 0;
         while (opti < args.length) {
@@ -11062,12 +11192,15 @@
                 dumpDalvik = true;
             } else if ("-d".equals(opt)) {
                 dumpDalvik = true;
+            } else if ("-c".equals(opt)) {
+                isCompact = true;
             } else if ("--oom".equals(opt)) {
                 oomOnly = true;
             } else if ("-h".equals(opt)) {
-                pw.println("meminfo dump options: [-a] [--oom] [process]");
+                pw.println("meminfo dump options: [-a] [-d] [-c] [--oom] [process]");
                 pw.println("  -a: include all available information for each process.");
                 pw.println("  -d: include dalvik details when dumping process details.");
+                pw.println("  -c: dump in a compact machine-parseable representation.");
                 pw.println("  --oom: only show processes organized by oom adj.");
                 pw.println("If [process] is specified it can be the name or ");
                 pw.println("pid of a specific process to dump.");
@@ -11090,10 +11223,9 @@
             dumpDetails = true;
         }
 
-        if (isCheckinRequest) {
+        if (isCheckinRequest || isCompact) {
             // short checkin version
-            pw.println(uptime + "," + realtime);
-            pw.flush();
+            pw.print("time,"); pw.print(uptime); pw.print(","); pw.println(realtime);
         } else {
             pw.println("Applications Memory Usage (kB):");
             pw.println("Uptime: " + uptime + " Realtime: " + realtime);
@@ -11109,26 +11241,31 @@
         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;
 
         Debug.MemoryInfo mi = null;
         for (int i = procs.size() - 1 ; i >= 0 ; i--) {
-            ProcessRecord r = procs.get(i);
-            IApplicationThread thread;
-            int oomAdj;
+            final ProcessRecord r = procs.get(i);
+            final IApplicationThread thread;
+            final int pid;
+            final int oomAdj;
+            final boolean hasActivities;
             synchronized (this) {
                 thread = r.thread;
+                pid = r.pid;
                 oomAdj = r.getSetAdjWithServices();
+                hasActivities = r.hasActivities;
             }
             if (thread != null) {
                 if (!isCheckinRequest && dumpDetails) {
-                    pw.println("\n** MEMINFO in pid " + r.pid + " [" + r.processName + "] **");
-                    pw.flush();
+                    pw.println("\n** MEMINFO in pid " + pid + " [" + r.processName + "] **");
                 }
                 if (dumpDetails) {
                     try {
+                        pw.flush();
                         mi = null;
                         mi = thread.dumpMemInfo(fd, isCheckinRequest, true, dumpDalvik, innerArgs);
                     } catch (RemoteException e) {
@@ -11142,25 +11279,28 @@
                         mi = new Debug.MemoryInfo();
                     }
                     if (!brief && !oomOnly) {
-                        Debug.getMemoryInfo(r.pid, mi);
+                        Debug.getMemoryInfo(pid, mi);
                     } else {
-                        mi.dalvikPss = (int)Debug.getPss(r.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);
                     }
                 }
 
                 if (!isCheckinRequest && mi != null) {
                     totalPss += myTotalPss;
-                    MemItem pssItem = new MemItem(r.processName + " (pid " + r.pid + ")",
-                            r.processName, myTotalPss, 0);
+                    MemItem pssItem = new MemItem(r.processName + " (pid " + pid +
+                            (hasActivities ? " / activities)" : ")"),
+                            r.processName, myTotalPss, pid, hasActivities);
                     procMems.add(pssItem);
 
                     nativePss += mi.nativePss;
@@ -11177,7 +11317,7 @@
                     }
 
                     for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) {
-                        if (r.setAdj <= DUMP_MEM_OOM_ADJ[oomIndex]
+                        if (oomAdj <= DUMP_MEM_OOM_ADJ[oomIndex]
                                 || oomIndex == (oomPss.length-1)) {
                             oomPss[oomIndex] += myTotalPss;
                             if (oomProcs[oomIndex] == null) {
@@ -11205,7 +11345,8 @@
             ArrayList<MemItem> oomMems = new ArrayList<MemItem>();
             for (int j=0; j<oomPss.length; j++) {
                 if (oomPss[j] != 0) {
-                    String label = DUMP_MEM_OOM_LABEL[j];
+                    String label = isCompact ? DUMP_MEM_OOM_COMPACT_LABEL[j]
+                            : DUMP_MEM_OOM_LABEL[j];
                     MemItem item = new MemItem(label, label, oomPss[j],
                             DUMP_MEM_OOM_ADJ[j]);
                     item.subitems = oomProcs[j];
@@ -11275,29 +11416,45 @@
                 }
             }
 
-            if (!brief && !oomOnly) {
+            if (!brief && !oomOnly && !isCompact) {
                 pw.println();
                 pw.println("Total PSS by process:");
-                dumpMemItems(pw, "  ", procMems, true);
+                dumpMemItems(pw, "  ", "proc", procMems, true, isCompact);
                 pw.println();
             }
-            pw.println("Total PSS by OOM adjustment:");
-            dumpMemItems(pw, "  ", oomMems, false);
+            if (!isCompact) {
+                pw.println("Total PSS by OOM adjustment:");
+            }
+            dumpMemItems(pw, "  ", "oom", oomMems, false, isCompact);
             if (!brief && !oomOnly) {
                 PrintWriter out = categoryPw != null ? categoryPw : pw;
-                out.println();
-                out.println("Total PSS by category:");
-                dumpMemItems(out, "  ", catMems, true);
+                if (!isCompact) {
+                    out.println();
+                    out.println("Total PSS by category:");
+                }
+                dumpMemItems(out, "  ", "cat", catMems, true, isCompact);
             }
-            pw.println();
+            if (!isCompact) {
+                pw.println();
+            }
             if (!brief) {
                 MemInfoReader memInfo = new MemInfoReader();
                 memInfo.readMemInfo();
-                pw.print("Total RAM: "); pw.print(memInfo.getTotalSize()/1024); pw.println(" kB");
-                pw.print(" Free RAM: "); pw.print(cachedPss + (memInfo.getCachedSize()/1024)
-                        + (memInfo.getFreeSize()/1024)); pw.println(" kB");
+                if (!isCompact) {
+                    pw.print("Total RAM: "); pw.print(memInfo.getTotalSize()/1024);
+                    pw.println(" kB");
+                    pw.print(" Free RAM: "); pw.print(cachedPss + (memInfo.getCachedSize()/1024)
+                            + (memInfo.getFreeSize()/1024)); pw.println(" kB");
+                } else {
+                    pw.print("ram,"); pw.print(memInfo.getTotalSize()/1024); pw.print(",");
+                    pw.print(cachedPss + (memInfo.getCachedSize()/1024)
+                            + (memInfo.getFreeSize()/1024)); pw.print(",");
+                    pw.println(totalPss - cachedPss);
+                }
             }
-            pw.print(" Used PSS: "); pw.print(totalPss - cachedPss); pw.println(" kB");
+            if (!isCompact) {
+                pw.print(" Used PSS: "); pw.print(totalPss - cachedPss); pw.println(" kB");
+            }
             if (!brief) {
                 final int[] SINGLE_LONG_FORMAT = new int[] {
                     Process.PROC_SPACE_TERM|Process.PROC_OUT_LONG
@@ -11318,11 +11475,18 @@
                 Process.readProcFile("/sys/kernel/mm/ksm/pages_volatile",
                         SINGLE_LONG_FORMAT, null, longOut, null);
                 long voltile = longOut[0] * ProcessList.PAGE_SIZE / 1024;
-                if (sharing != 0 || shared != 0 || unshared != 0 || voltile != 0) {
-                    pw.print("      KSM: "); pw.print(sharing); pw.print(" kB saved from shared ");
-                            pw.print(shared); pw.println(" kB");
-                    pw.print("           "); pw.print(unshared); pw.print(" kB unshared; ");
-                            pw.print(voltile); pw.println(" kB volatile");
+                if (!isCompact) {
+                    if (sharing != 0 || shared != 0 || unshared != 0 || voltile != 0) {
+                        pw.print("      KSM: "); pw.print(sharing);
+                                pw.print(" kB saved from shared ");
+                                pw.print(shared); pw.println(" kB");
+                        pw.print("           "); pw.print(unshared); pw.print(" kB unshared; ");
+                                pw.print(voltile); pw.println(" kB volatile");
+                    }
+                } else {
+                    pw.print("ksm,"); pw.print(sharing); pw.print(",");
+                    pw.print(shared); pw.print(","); pw.print(unshared); pw.print(",");
+                    pw.println(voltile);
                 }
             }
         }
@@ -11497,14 +11661,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 "
@@ -13264,28 +13425,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_EMPTY;
             return (app.curAdj=app.curRawAdj=ProcessList.CACHED_APP_MAX_ADJ);
         }
 
@@ -13303,11 +13453,12 @@
             // 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;
             app.curSchedGroup = Process.THREAD_GROUP_DEFAULT;
+            app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT;
             // System process can do UI, and when they do we want to have
             // them trim their memory after the user leaves the UI.  To
             // facilitate this, here we need to determine whether or not it
@@ -13327,6 +13478,9 @@
                     }
                 }
             }
+            if (!app.systemNoUi) {
+                app.curProcState = ActivityManager.PROCESS_STATE_PERSISTENT_UI;
+            }
             return (app.curAdj=app.maxAdj);
         }
 
@@ -13338,6 +13492,7 @@
         // important to least, and assign an appropriate OOM adjustment.
         int adj;
         int schedGroup;
+        int procState;
         boolean foregroundActivities = false;
         boolean interesting = false;
         BroadcastQueue queue;
@@ -13349,12 +13504,14 @@
             foregroundActivities = true;
             interesting = true;
             app.hasActivities = true;
+            procState = ActivityManager.PROCESS_STATE_TOP;
         } else if (app.instrumentationClass != null) {
             // Don't want to kill running instrumentation.
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             app.adjType = "instrumentation";
             interesting = true;
+            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.
@@ -13364,36 +13521,48 @@
             schedGroup = (queue == mFgBroadcastQueue)
                     ? Process.THREAD_GROUP_DEFAULT : Process.THREAD_GROUP_BG_NONINTERACTIVE;
             app.adjType = "broadcast";
+            procState = ActivityManager.PROCESS_STATE_RECEIVER;
         } else if (app.executingServices.size() > 0) {
             // An app that is currently executing a service callback also
             // counts as being in the foreground.
             adj = ProcessList.FOREGROUND_APP_ADJ;
             schedGroup = Process.THREAD_GROUP_DEFAULT;
             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";
+            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_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) {
@@ -13401,32 +13570,39 @@
                         adj = ProcessList.PERCEPTIBLE_APP_ADJ;
                         app.adjType = "pausing";
                     }
+                    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 (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";
             }
         }
 
@@ -13434,12 +13610,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_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_FOREGROUND;
                 app.cached = false;
                 app.adjType = "force-fg";
                 app.adjSource = app.forcingToForeground;
@@ -13451,32 +13629,46 @@
             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 (adj > ProcessList.HOME_APP_ADJ && app == mHomeProcess) {
-            // This process is hosting what we currently consider to be the
-            // home app, so we don't want to let it go into the background.
-            adj = ProcessList.HOME_APP_ADJ;
-            schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.cached = false;
-            app.adjType = "home";
+        if (app == mHomeProcess) {
+            if (adj > ProcessList.HOME_APP_ADJ) {
+                // This process is hosting what we currently consider to be the
+                // home app, so we don't want to let it go into the background.
+                adj = ProcessList.HOME_APP_ADJ;
+                schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+                app.cached = false;
+                app.adjType = "home";
+            }
+            if (procState > ActivityManager.PROCESS_STATE_HOME) {
+                procState = ActivityManager.PROCESS_STATE_HOME;
+            }
         }
 
-        if (adj > ProcessList.PREVIOUS_APP_ADJ && app == mPreviousProcess
-                && app.activities.size() > 0) {
-            // This was the previous process that showed UI to the user.
-            // We want to try to keep it around more aggressively, to give
-            // a good experience around switching between two apps.
-            adj = ProcessList.PREVIOUS_APP_ADJ;
-            schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
-            app.cached = false;
-            app.adjType = "previous";
+        if (app == mPreviousProcess && app.activities.size() > 0) {
+            if (adj > ProcessList.PREVIOUS_APP_ADJ) {
+                // This was the previous process that showed UI to the user.
+                // We want to try to keep it around more aggressively, to give
+                // a good experience around switching between two apps.
+                adj = ProcessList.PREVIOUS_APP_ADJ;
+                schedGroup = Process.THREAD_GROUP_BG_NONINTERACTIVE;
+                app.cached = false;
+                app.adjType = "previous";
+            }
+            if (procState > ActivityManager.PROCESS_STATE_LAST_ACTIVITY) {
+                procState = ActivityManager.PROCESS_STATE_LAST_ACTIVITY;
+            }
         }
 
         if (false) Slog.i(TAG, "OOM " + app + ": initial adj=" + adj
@@ -13487,7 +13679,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) {
@@ -13495,230 +13687,237 @@
             if (adj > ProcessList.BACKUP_APP_ADJ) {
                 if (DEBUG_BACKUP) Slog.v(TAG, "oom BACKUP_APP_ADJ for " + app);
                 adj = ProcessList.BACKUP_APP_ADJ;
+                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) {
+                    procState = ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND;
+                }
                 app.adjType = "backup";
                 app.cached = false;
             }
+            if (procState > ActivityManager.PROCESS_STATE_BACKUP) {
+                procState = ActivityManager.PROCESS_STATE_BACKUP;
+            }
         }
 
-        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 (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.
+        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;
+                }
+                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) {
-                            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";
+                            adj = ProcessList.SERVICE_ADJ;
+                            app.adjType = "started-services";
+                            app.cached = false;
                         }
                     }
-                    // Don't kill this process because it is doing work; it
-                    // has said it is doing work.
-                    app.keeping = true;
-                }
-                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 ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) {
-                            ProcessRecord client = cr.binding.client;
-                            int clientAdj = adj;
-                            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;
-                                }
-                            }
-                            clientAdj = computeOomAdjLocked(client, myCachedAdj,
-                                    myClientCachedAdj, myEmptyAdj, TOP_APP, true, doingAll);
-                            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;
-                                } 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 I'll 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 (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;
-                            }
-                            if ((cr.flags&Context.BIND_NOT_FOREGROUND) == 0) {
-                                if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
-                                    schedGroup = Process.THREAD_GROUP_DEFAULT;
-                                }
-                            }
-                        }
-                        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;
+                        if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+                            // If the other app is cached for any reason, for purposes here
+                            // we are going to consider it empty.  The specific cached state
+                            // doesn't propagate except under certain conditions.
+                            clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
+                        }
+                        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 = true;
+                                }
+                            }
+                        }
+                        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;
@@ -13726,32 +13925,13 @@
                     // 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 clientAdj = computeOomAdjLocked(client, cachedAdj, TOP_APP, doingAll, now);
+                int clientProcState = client.curProcState;
+                if (clientProcState >= ActivityManager.PROCESS_STATE_CACHED_ACTIVITY) {
+                    // If the other app is cached for any reason, for purposes here
+                    // we are going to consider it empty.
+                    clientProcState = ActivityManager.PROCESS_STATE_CACHED_EMPTY;
                 }
-                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);
                 if (adj > clientAdj) {
                     if (app.hasShownUi && app != mHomeProcess
                             && clientAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {
@@ -13761,18 +13941,18 @@
                                 ? 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;
                     app.adjSourceOom = clientAdj;
                     app.adjTarget = cpr.name;
                 }
+                if (procState > clientProcState) {
+                    procState = clientProcState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+                            ? clientProcState : ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                }
                 if (client.curSchedGroup == Process.THREAD_GROUP_DEFAULT) {
                     schedGroup = Process.THREAD_GROUP_DEFAULT;
                 }
@@ -13789,6 +13969,9 @@
                     app.adjType = "provider";
                     app.adjTarget = cpr.name;
                 }
+                if (procState > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) {
+                    procState = ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND;
+                }
             }
         }
 
@@ -13804,16 +13987,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 +
@@ -13828,24 +14001,14 @@
             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;
 
         int importance = app.memImportance;
         if (importance == 0 || adj != app.curAdj || schedGroup != app.curSchedGroup) {
@@ -14199,22 +14362,10 @@
         }
     }
 
-    private final boolean updateOomAdjLocked(ProcessRecord app, int cachedAdj,
-            int clientCachedAdj, int emptyAdj, ProcessRecord TOP_APP, boolean doingAll, 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
@@ -14258,11 +14409,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);
@@ -14301,29 +14447,75 @@
                         }
                     }
                 }
+                Process.setSwappiness(app.pid,
+                        app.curSchedGroup <= Process.THREAD_GROUP_BG_NONINTERACTIVE);
+            }
+        }
+        if (app.repProcState != app.curProcState) {
+            app.repProcState = app.curProcState;
+            if (!reportingProcessState && app.thread != null) {
+                try {
+                    if (false) {
+                        //RuntimeException h = new RuntimeException("here");
+                        Slog.i(TAG, "Sending new process state " + app.repProcState
+                                + " to " + app /*, h*/);
+                    }
+                    app.thread.setProcessState(app.repProcState);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+        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();
     }
 
     final boolean updateOomAdjLocked(ProcessRecord app) {
+        return updateOomAdjLocked(app, false);
+    }
+
+    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, 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();
@@ -14336,6 +14528,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();
@@ -14364,7 +14557,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.
@@ -14389,80 +14582,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 curClientCachedAdj = 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=N-1; i>=0; 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, 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
@@ -14484,8 +14694,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
@@ -14500,8 +14714,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++;
                 }
@@ -14516,11 +14730,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++;
@@ -14528,6 +14741,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;
@@ -14539,13 +14753,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) {
                         }
@@ -14576,10 +14796,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) {
@@ -14587,14 +14810,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) {
                             }
@@ -14603,6 +14829,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) {
                         }
@@ -14611,14 +14840,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) {
@@ -14636,16 +14872,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/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 4f09407..b858755 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -17,6 +17,7 @@
 package com.android.server.am;
 
 import android.os.Trace;
+import com.android.internal.R.styleable;
 import com.android.internal.app.ResolverActivity;
 import com.android.server.AttributeCache;
 import com.android.server.am.ActivityStack.ActivityState;
@@ -338,6 +339,10 @@
         }
     }
 
+    boolean isNotResolverActivity() {
+        return !ResolverActivity.class.getName().equals(realActivity.getClassName());
+    }
+
     ActivityRecord(ActivityManagerService _service, ProcessRecord _caller,
             int _launchedFromUid, String _launchedFromPackage, Intent _intent, String _resolvedType,
             ActivityInfo aInfo, Configuration _configuration,
@@ -431,7 +436,7 @@
             launchMode = aInfo.launchMode;
 
             AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
-                    realTheme, com.android.internal.R.styleable.Window);
+                    realTheme, com.android.internal.R.styleable.Window, userId);
             fullscreen = ent != null && !ent.array.getBoolean(
                     com.android.internal.R.styleable.Window_windowIsFloating, false)
                     && !ent.array.getBoolean(
@@ -441,21 +446,22 @@
 
             // If we know the system has determined the component, then
             // we can consider this to be a home activity...
-            // Note the last check is so we don't count the resolver
-            // activity as being home...  really, we don't care about
-            // doing anything special with something that comes from
-            // the core framework package.
-            if ((!_componentSpecified || _launchedFromUid == Process.myUid()
+            String homePackageName = supervisor.getHomePackageName();
+            if (homePackageName != null && homePackageName.equals(packageName)) {
+                mActivityType = HOME_ACTIVITY_TYPE;
+            } else if ((!_componentSpecified || _launchedFromUid == Process.myUid()
                     || _launchedFromUid == 0) &&
                     Intent.ACTION_MAIN.equals(_intent.getAction()) &&
                     _intent.hasCategory(Intent.CATEGORY_HOME) &&
                     _intent.getCategories().size() == 1 &&
                     _intent.getData() == null &&
                     _intent.getType() == null &&
-                    (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
-                    !ResolverActivity.class.getName().equals(realActivity.getClassName())) {
+                    (intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
                 // This sure looks like a home activity!
                 mActivityType = HOME_ACTIVITY_TYPE;
+                if (isNotResolverActivity()) {
+                    supervisor.setHomePackageName(userId, packageName);
+                }
             } else if (realActivity.getClassName().contains("com.android.systemui.recent")) {
                 mActivityType = RECENTS_ACTIVITY_TYPE;
             } else {
@@ -505,19 +511,23 @@
         }
     }
 
-    boolean convertToOpaque() {
-        if (fullscreen) {
+    boolean changeWindowTranslucency(boolean toOpaque) {
+        if (fullscreen == toOpaque) {
+            return false;
+        }
+        AttributeCache.Entry ent =
+                AttributeCache.instance().get(packageName, realTheme, styleable.Window, userId);
+        if (ent == null
+                || !ent.array.getBoolean(styleable.Window_windowIsTranslucent, false)
+                || ent.array.getBoolean(styleable.Window_windowIsFloating, false)) {
             return false;
         }
 
-        AttributeCache.Entry ent = AttributeCache.instance().get(packageName,
-                realTheme, com.android.internal.R.styleable.Window);
-        if (ent != null && !ent.array.getBoolean(
-                com.android.internal.R.styleable.Window_windowIsFloating, false)) {
-            fullscreen = true;
-            ++task.numFullscreen;
-        }
-        return fullscreen;
+        // Keep track of the number of fullscreen activities in this task.
+        task.numFullscreen += toOpaque ? +1 : -1;
+
+        fullscreen = toOpaque;
+        return true;
     }
 
     void putInHistory() {
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 98b3ce9..dd71044 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -118,6 +118,10 @@
     // is being started.
     static final boolean SHOW_APP_STARTING_PREVIEW = true;
 
+    // How long to wait for all background Activities to redraw following a call to
+    // convertToTranslucent().
+    static final long TRANSLUCENT_CONVERSION_TIMEOUT = 2000;
+
     enum ActivityState {
         INITIALIZING,
         RESUMED,
@@ -184,6 +188,16 @@
      */
     ActivityRecord mLastStartedActivity = null;
 
+    // The topmost Activity passed to convertToTranslucent(). When non-null it means we are
+    // waiting for all Activities in mUndrawnActivitiesBelowTopTranslucent to be removed as they
+    // are drawn. When the last member of mUndrawnActivitiesBelowTopTranslucent is removed the
+    // Activity in mTranslucentActivityWaiting is notified via
+    // Activity.onTranslucentConversionComplete(false). If a timeout occurs prior to the last
+    // background activity being drawn then the same call will be made with a true value.
+    ActivityRecord mTranslucentActivityWaiting = null;
+    ArrayList<ActivityRecord> mUndrawnActivitiesBelowTopTranslucent =
+            new ArrayList<ActivityRecord>();
+
     /**
      * Set when we know we are going to be calling updateConfiguration()
      * soon, so want to skip intermediate config checks.
@@ -215,6 +229,7 @@
     static final int LAUNCH_TICK_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 3;
     static final int STOP_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 4;
     static final int DESTROY_ACTIVITIES_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 5;
+    static final int TRANSLUCENT_TIMEOUT_MSG = ActivityManagerService.FIRST_ACTIVITY_STACK_MSG + 6;
 
     static class ScheduleDestroyArgs {
         final ProcessRecord mOwner;
@@ -285,7 +300,12 @@
                     synchronized (mService) {
                         destroyActivitiesLocked(args.mOwner, args.mOomAdj, args.mReason);
                     }
-                }
+                } break;
+                case TRANSLUCENT_TIMEOUT_MSG: {
+                    synchronized (mService) {
+                        notifyActivityDrawnLocked(null);
+                    }
+                } break;
             }
         }
     }
@@ -445,7 +465,9 @@
      * Returns the top activity in any existing task matching the given
      * Intent.  Returns null if no such task is found.
      */
-    ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
+    ActivityRecord findTaskLocked(ActivityRecord target) {
+        Intent intent = target.intent;
+        ActivityInfo info = target.info;
         ComponentName cls = intent.getComponent();
         if (info.targetActivity != null) {
             cls = new ComponentName(info.packageName, info.targetActivity);
@@ -454,6 +476,10 @@
 
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
+            if (task.userId != userId) {
+                // Looking for a different task.
+                continue;
+            }
             final ActivityRecord r = task.getTopActivity();
             if (r == null || r.finishing || r.userId != userId ||
                     r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
@@ -498,7 +524,11 @@
         final int userId = UserHandle.getUserId(info.applicationInfo.uid);
 
         for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
-            final ArrayList<ActivityRecord> activities = mTaskHistory.get(taskNdx).mActivities;
+            TaskRecord task = mTaskHistory.get(taskNdx);
+            if (task.userId != mCurrentUser) {
+                return null;
+            }
+            final ArrayList<ActivityRecord> activities = task.mActivities;
             for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {
                 ActivityRecord r = activities.get(activityNdx);
                 if (!r.finishing && r.intent.getComponent().equals(cls) && r.userId == userId) {
@@ -514,8 +544,7 @@
     }
 
     /*
-     * Move the activities around in the stack to bring a user to the foreground. This only
-     * matters on the home stack. All other stacks are single user.
+     * Move the activities around in the stack to bring a user to the foreground.
      * @return whether there are any activities for the specified user.
      */
     final boolean switchUserLocked(int userId) {
@@ -791,21 +820,7 @@
                     destroyActivityLocked(r, true, false, "stop-config");
                     mStackSupervisor.resumeTopActivitiesLocked();
                 } else {
-                    // Now that this process has stopped, we may want to consider
-                    // it to be the previous app to try to keep around in case
-                    // the user wants to return to it.
-                    ProcessRecord fgApp = null;
-                    if (mResumedActivity != null) {
-                        fgApp = mResumedActivity.app;
-                    } else if (mPausingActivity != null) {
-                        fgApp = mPausingActivity.app;
-                    }
-                    if (r.app != null && fgApp != null && r.app != fgApp
-                            && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
-                            && r.app != mService.mHomeProcess) {
-                        mService.mPreviousProcess = r.app;
-                        mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
-                    }
+                    mStackSupervisor.updatePreviousProcessLocked(r);
                 }
             }
         }
@@ -952,6 +967,16 @@
                 TAG, "ensureActivitiesVisible behind " + top
                 + " configChanges=0x" + Integer.toHexString(configChanges));
 
+        if (mTranslucentActivityWaiting != top) {
+            mUndrawnActivitiesBelowTopTranslucent.clear();
+            if (mTranslucentActivityWaiting != null) {
+                // Call the callback with a timeout indication.
+                notifyActivityDrawnLocked(null);
+                mTranslucentActivityWaiting = null;
+            }
+            mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+        }
+
         // If the top activity is not fullscreen, then we need to
         // make sure any activities under it are now visible.
         boolean aboveTop = true;
@@ -1018,6 +1043,9 @@
                             if (DEBUG_VISBILITY) Slog.v(
                                     TAG, "Making visible and scheduling visibility: " + r);
                             try {
+                                if (mTranslucentActivityWaiting != null) {
+                                    mUndrawnActivitiesBelowTopTranslucent.add(r);
+                                }
                                 mWindowManager.setAppVisibility(r.appToken, true);
                                 r.sleeping = false;
                                 r.app.pendingUiClean = true;
@@ -1070,6 +1098,9 @@
                                 case RESUMED:
                                 case PAUSING:
                                 case PAUSED:
+                                    // This case created for transitioning activities from
+                                    // translucent to opaque {@link Activity#convertToOpaque}.
+                                    mStackSupervisor.mStoppingActivities.remove(r);
                                     stopActivityLocked(r);
                                     break;
 
@@ -1091,6 +1122,42 @@
         return showHomeBehindStack;
     }
 
+    void convertToTranslucent(ActivityRecord r) {
+        mTranslucentActivityWaiting = r;
+        mUndrawnActivitiesBelowTopTranslucent.clear();
+        mHandler.sendEmptyMessageDelayed(TRANSLUCENT_TIMEOUT_MSG, TRANSLUCENT_CONVERSION_TIMEOUT);
+    }
+
+    /**
+     * Called as activities below the top translucent activity are redrawn. When the last one is
+     * redrawn notify the top activity by calling
+     * {@link Activity#onTranslucentConversionComplete}.
+     *
+     * @param r The most recent background activity to be drawn. Or, if r is null then a timeout
+     * occurred and the activity will be notified immediately.
+     */
+    void notifyActivityDrawnLocked(ActivityRecord r) {
+        if ((r == null)
+                || (mUndrawnActivitiesBelowTopTranslucent.remove(r) &&
+                        mUndrawnActivitiesBelowTopTranslucent.isEmpty())) {
+            // The last undrawn activity below the top has just been drawn. If there is an
+            // opaque activity at the top, notify it that it can become translucent safely now.
+            final ActivityRecord waitingActivity = mTranslucentActivityWaiting;
+            mTranslucentActivityWaiting = null;
+            mUndrawnActivitiesBelowTopTranslucent.clear();
+            mHandler.removeMessages(TRANSLUCENT_TIMEOUT_MSG);
+
+            if (waitingActivity != null && waitingActivity.app != null &&
+                    waitingActivity.app.thread != null) {
+                try {
+                    waitingActivity.app.thread.scheduleTranslucentConversionComplete(
+                            waitingActivity.appToken, r != null);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+    }
+
     /**
      * Ensure that the top activity in the stack is resumed.
      *
@@ -1117,6 +1184,7 @@
             // There are no more activities!  Let's just start up the
             // Launcher...
             ActivityOptions.abort(options);
+            if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: No more activities go home");
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return mStackSupervisor.resumeHomeActivity(prev);
         }
@@ -1131,6 +1199,7 @@
             mWindowManager.executeAppTransition();
             mNoAnimActivities.clear();
             ActivityOptions.abort(options);
+            if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Top activity resumed " + next);
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return false;
         }
@@ -1158,6 +1227,7 @@
                 final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
                 mTaskHistory.get(taskNdx).mActivities.get(0).mLaunchHomeTaskNext = true;
             } else {
+                if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Launching home next");
                 return mStackSupervisor.resumeHomeActivity(prev);
             }
         }
@@ -1172,6 +1242,7 @@
             mWindowManager.executeAppTransition();
             mNoAnimActivities.clear();
             ActivityOptions.abort(options);
+            if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Going to sleep and all paused");
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return false;
         }
@@ -1200,7 +1271,8 @@
         // If we are currently pausing an activity, then don't do anything
         // until that is done.
         if (!mStackSupervisor.allPausedActivitiesComplete()) {
-            if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v(TAG, "Skip resume: some activity pausing");
+            if (DEBUG_SWITCH || DEBUG_PAUSE || DEBUG_STATES) Slog.v(TAG,
+                    "resumeTopActivityLocked: Skip resume: some activity pausing.");
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             return false;
         }
@@ -1240,9 +1312,11 @@
         if (mResumedActivity != null) {
             pausing = true;
             startPausingLocked(userLeaving, false);
+            if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Pausing " + mResumedActivity);
         }
         if (pausing) {
-            if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing");
+            if (DEBUG_SWITCH || DEBUG_STATES) Slog.v(TAG,
+                    "resumeTopActivityLocked: Skip resume: need to start pausing");
             // At this point we want to put the upcoming activity's process
             // at the top of the LRU list, since we know we will be needing it
             // very soon and it would be a waste to let it get killed if it
@@ -1404,7 +1478,7 @@
                 // is still at the top and schedule another run if something
                 // weird happened.
                 ActivityRecord nextNext = topRunningActivityLocked(null);
-                if (DEBUG_SWITCH) Slog.i(TAG,
+                if (DEBUG_SWITCH || DEBUG_STATES) Slog.i(TAG,
                         "Activity config changed during resume: " + next
                         + ", new next: " + nextNext);
                 if (nextNext != next) {
@@ -1444,11 +1518,13 @@
                 next.sleeping = false;
                 mService.showAskCompatModeDialogLocked(next);
                 next.app.pendingUiClean = true;
-                next.app.thread.scheduleResumeActivity(next.appToken,
+                next.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
+                next.app.thread.scheduleResumeActivity(next.appToken, next.app.repProcState,
                         mService.isNextTransitionForward());
 
                 mStackSupervisor.checkReadyForSleepLocked();
 
+                if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Resumed " + next);
             } catch (Exception e) {
                 // Whoops, need to restart this activity!
                 if (DEBUG_STATES) Slog.v(TAG, "Resume failed; resetting state to "
@@ -1505,6 +1581,7 @@
                 }
                 if (DEBUG_SWITCH) Slog.v(TAG, "Restarting: " + next);
             }
+            if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: Restarting " + next);
             mStackSupervisor.startSpecificActivityLocked(next, true, true);
         }
 
@@ -1512,6 +1589,21 @@
         return true;
     }
 
+    private void insertTaskAtTop(TaskRecord task) {
+        mTaskHistory.remove(task);
+        // Now put task at top.
+        int stackNdx = mTaskHistory.size();
+        if (task.userId != mCurrentUser) {
+            // Put non-current user tasks below current user tasks.
+            while (--stackNdx >= 0) {
+                if (mTaskHistory.get(stackNdx).userId != mCurrentUser) {
+                    break;
+                }
+            }
+            ++stackNdx;
+        }
+        mTaskHistory.add(stackNdx, task);
+    }
 
     final void startActivityLocked(ActivityRecord r, boolean newTask,
             boolean doResume, boolean keepCurTransition, Bundle options) {
@@ -1521,9 +1613,7 @@
             // Last activity in task had been removed or ActivityManagerService is reusing task.
             // Insert or replace.
             // Might not even be in.
-            mTaskHistory.remove(rTask);
-            // Now put task at top.
-            mTaskHistory.add(rTask);
+            insertTaskAtTop(rTask);
             mWindowManager.moveTaskToTop(taskId);
         }
         TaskRecord task = null;
@@ -1543,7 +1633,8 @@
                         r.putInHistory();
                         mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                                 r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
-                                (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
+                                (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0,
+                                r.userId);
                         if (VALIDATE_TOKENS) {
                             validateAppTokensLocked();
                         }
@@ -1604,7 +1695,7 @@
             r.updateOptionsLocked(options);
             mWindowManager.addAppToken(task.mActivities.indexOf(r),
                     r.appToken, r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
-                    (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
+                    (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId);
             boolean doShow = true;
             if (newTask) {
                 // Even though this activity is starting fresh, we still need
@@ -1647,7 +1738,7 @@
             // because there is nothing for it to animate on top of.
             mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
                     r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
-                    (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0);
+                    (r.info.flags & ActivityInfo.FLAG_SHOW_ON_LOCK_SCREEN) != 0, r.userId);
             ActivityOptions.abort(options);
         }
         if (VALIDATE_TOKENS) {
@@ -2841,8 +2932,7 @@
 
         // Shift all activities with this task up to the top
         // of the stack, keeping them in the same internal order.
-        mTaskHistory.remove(tr);
-        mTaskHistory.add(tr);
+        insertTaskAtTop(tr);
 
         if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare to front transition: task=" + tr);
         if (reason != null &&
@@ -3324,7 +3414,7 @@
             printed |= ActivityStackSupervisor.dumpHistoryList(fd, pw,
                     mTaskHistory.get(taskNdx).mActivities, "    ", "Hist", true, !dumpAll,
                     dumpClient, dumpPackage, needSep, header,
-                    "    Task " + taskNdx + ": id #" + task.taskId);
+                    "    Task id #" + task.taskId);
             if (printed) {
                 header = null;
             }
@@ -3392,12 +3482,8 @@
     }
 
     TaskRecord createTaskRecord(int taskId, ActivityInfo info, Intent intent, boolean toTop) {
-        TaskRecord task = new TaskRecord(taskId, info, intent, this);
-        if (toTop) {
-            mTaskHistory.add(task);
-        } else {
-            mTaskHistory.add(0, task);
-        }
+        TaskRecord task = new TaskRecord(taskId, info, intent);
+        addTask(task, toTop);
         return task;
     }
 
@@ -3408,7 +3494,7 @@
     void addTask(final TaskRecord task, final boolean toTop) {
         task.stack = this;
         if (toTop) {
-            mTaskHistory.add(task);
+            insertTaskAtTop(task);
         } else {
             mTaskHistory.add(0, task);
         }
diff --git a/services/java/com/android/server/am/ActivityStackSupervisor.java b/services/java/com/android/server/am/ActivityStackSupervisor.java
index 63f91ac..925fb3f 100644
--- a/services/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/java/com/android/server/am/ActivityStackSupervisor.java
@@ -185,9 +185,6 @@
      * is being brought in front of us. */
     boolean mUserLeaving = false;
 
-    /** Stacks belonging to users other than mCurrentUser. Indexed by userId. */
-    final SparseArray<UserState> mUserStates = new SparseArray<UserState>();
-
     /** Set when we have taken too long waiting to go to sleep. */
     boolean mSleepTimeout = false;
 
@@ -206,6 +203,12 @@
      */
     final PowerManager.WakeLock mGoingToSleep;
 
+    /**
+     * The name of the current home activity for each user.
+     * TODO: Remove entries when user is deleted.
+     */
+    final SparseArray<String> mHomePackageNames = new SparseArray<String>();
+
     public ActivityStackSupervisor(ActivityManagerService service, Context context,
             Looper looper) {
         mService = service;
@@ -263,8 +266,7 @@
     }
 
     boolean isFrontStack(ActivityStack stack) {
-        return (stack.mCurrentUser == mCurrentUser) &&
-                !(stack.isHomeStack() ^ getFocusedStack().isHomeStack());
+        return !(stack.isHomeStack() ^ getFocusedStack().isHomeStack());
     }
 
     void moveHomeStack(boolean toFront) {
@@ -354,7 +356,7 @@
             final int stackId = stack.mStackId;
             final int nextStackId = mWindowManager.removeStack(stackId);
             // TODO: Perhaps we need to let the ActivityManager determine the next focus...
-            if (getFocusedStack().mStackId == stackId) {
+            if (mFocusedStack == null || mFocusedStack.mStackId == stackId) {
                 // If this is the last app stack, set mFocusedStack to null.
                 mFocusedStack = nextStackId == HOME_STACK_ID ? null : getStack(nextStackId);
             }
@@ -467,6 +469,8 @@
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = mStacks.get(stackNdx);
             if (!isFrontStack(stack) && stack.mResumedActivity != null) {
+                if (DEBUG_STATES) Slog.d(TAG, "pauseBackStacks: stack=" + stack +
+                        " mResumedActivity=" + stack.mResumedActivity);
                 stack.startPausingLocked(userLeaving, false);
                 someActivityPaused = true;
             }
@@ -475,16 +479,22 @@
     }
 
     boolean allPausedActivitiesComplete() {
+        boolean pausing = true;
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = mStacks.get(stackNdx);
             final ActivityRecord r = stack.mPausingActivity;
             if (r != null && r.state != ActivityState.PAUSED
                     && r.state != ActivityState.STOPPED
                     && r.state != ActivityState.STOPPING) {
-                return false;
+                if (DEBUG_STATES) {
+                    Slog.d(TAG, "allPausedActivitiesComplete: r=" + r + " state=" + r.state);
+                    pausing = false;
+                } else {
+                    return false;
+                }
             }
         }
-        return true;
+        return pausing;
     }
 
     void reportActivityVisibleLocked(ActivityRecord r) {
@@ -524,8 +534,7 @@
 
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
             final ActivityStack stack = mStacks.get(stackNdx);
-            if (stack.mCurrentUser == mCurrentUser && stack != focusedStack &&
-                    isFrontStack(stack)) {
+            if (stack != focusedStack && isFrontStack(stack)) {
                 r = stack.topRunningActivityLocked(null);
                 if (r != null) {
                     return r;
@@ -895,7 +904,7 @@
                         r.userId, System.identityHashCode(r),
                         r.task.taskId, r.shortComponentName);
             }
-            if (r.isHomeActivity()) {
+            if (r.isHomeActivity() && r.isNotResolverActivity()) {
                 mService.mHomeProcess = app;
             }
             mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
@@ -929,10 +938,11 @@
                     }
                 }
             }
+            app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_TOP);
             app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                     System.identityHashCode(r), r.info,
-                    new Configuration(mService.mConfiguration),
-                    r.compat, r.icicle, results, newIntents, !andResume,
+                    new Configuration(mService.mConfiguration), r.compat,
+                    app.repProcState, r.icicle, results, newIntents, !andResume,
                     mService.isNextTransitionForward(), profileFile, profileFd,
                     profileAutoStop);
 
@@ -1215,28 +1225,43 @@
         return err;
     }
 
-    ActivityStack getCorrectStack(ActivityRecord r) {
+    ActivityStack adjustStackFocus(ActivityRecord r) {
         final TaskRecord task = r.task;
         if (r.isApplicationActivity() || (task != null && task.isApplicationTask())) {
-            int stackNdx;
-            for (stackNdx = mStacks.size() - 1; stackNdx > 0; --stackNdx) {
-                if (mStacks.get(stackNdx).mCurrentUser == mCurrentUser) {
-                    break;
+            if (task != null) {
+                if (mFocusedStack != task.stack) {
+                    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
+                            "adjustStackFocus: Setting focused stack to r=" + r + " task=" + task);
+                    mFocusedStack = task.stack;
+                } else {
+                    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
+                        "adjustStackFocus: Focused stack already=" + mFocusedStack);
+                }
+                return mFocusedStack;
+            }
+
+            if (mFocusedStack != null) {
+                if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
+                        "adjustStackFocus: Have a focused stack=" + mFocusedStack);
+                return mFocusedStack;
+            }
+
+            for (int stackNdx = mStacks.size() - 1; stackNdx > 0; --stackNdx) {
+                ActivityStack stack = mStacks.get(stackNdx);
+                if (!stack.isHomeStack()) {
+                    if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
+                            "adjustStackFocus: Setting focused stack=" + stack);
+                    mFocusedStack = stack;
+                    return mFocusedStack;
                 }
             }
-            if (stackNdx == 0) {
-                // Time to create the first app stack for this user.
-                int stackId = mService.createStack(-1, HOME_STACK_ID,
-                        StackBox.TASK_STACK_GOES_OVER, 1.0f);
-                if (DEBUG_FOCUS) Slog.d(TAG, "getCorrectStack: New stack r=" + r + " stackId="
-                        + stackId);
-                mFocusedStack = getStack(stackId);
-            }
-            if (task != null) {
-                if (DEBUG_FOCUS) Slog.d(TAG, "getCorrectStack: Setting focused stack to r=" +
-                        r + " task=" + task);
-                mFocusedStack = task.stack;
-            }
+
+            // Time to create the first app stack for this user.
+            int stackId = mService.createStack(-1, HOME_STACK_ID,
+                StackBox.TASK_STACK_GOES_OVER, 1.0f);
+            if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG, "adjustStackFocus: New stack r=" + r +
+                    " stackId=" + stackId);
+            mFocusedStack = getStack(stackId);
             return mFocusedStack;
         }
         return mHomeStack;
@@ -1255,8 +1280,9 @@
                 mStackState = STACK_STATE_HOME_TO_FRONT;
             }
         } else {
-            if (DEBUG_FOCUS) Slog.d(TAG, "setFocusedStack: Setting focused stack to r=" +
-                    r + " task=" + r.task + " Callers=" + Debug.getCallers(3));
+            if (DEBUG_FOCUS || DEBUG_STACK) Slog.d(TAG,
+                    "setFocusedStack: Setting focused stack to r=" + r + " task=" + r.task +
+                    " Callers=" + Debug.getCallers(3));
             mFocusedStack = r.task.stack;
             if (mStackState != STACK_STATE_HOME_IN_BACK) {
                 if (DEBUG_STACK) Slog.d(TAG, "setFocusedStack: mStackState old=" +
@@ -1365,7 +1391,7 @@
                 // instance of it in the history, and it is always in its own
                 // unique task, so we do a special search.
                 ActivityRecord intentActivity = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
-                        ? findTaskLocked(intent, r.info)
+                        ? findTaskLocked(r)
                         : findActivityLocked(intent, r.info);
                 if (intentActivity != null) {
                     if (r.task == null) {
@@ -1591,7 +1617,7 @@
         // Should this be considered a new task?
         if (r.resultTo == null && !addingToTask
                 && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
-            targetStack = getCorrectStack(r);
+            targetStack = adjustStackFocus(r);
             moveHomeStack(targetStack.isHomeStack());
             if (reuseTask == null) {
                 r.setTask(targetStack.createTaskRecord(getNextTaskId(), r.info, intent, true),
@@ -1670,7 +1696,7 @@
             // This not being started from an existing activity, and not part
             // of a new task...  just put it in the top task, though these days
             // this case should never happen.
-            targetStack = getCorrectStack(r);
+            targetStack = adjustStackFocus(r);
             moveHomeStack(targetStack.isHomeStack());
             ActivityRecord prev = targetStack.topActivity();
             r.setTask(prev != null ? prev.task
@@ -1893,6 +1919,37 @@
         return didSomething;
     }
 
+    void updatePreviousProcessLocked(ActivityRecord r) {
+        // Now that this process has stopped, we may want to consider
+        // it to be the previous app to try to keep around in case
+        // the user wants to return to it.
+
+        // First, found out what is currently the foreground app, so that
+        // we don't blow away the previous app if this activity is being
+        // hosted by the process that is actually still the foreground.
+        ProcessRecord fgApp = null;
+        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            final ActivityStack stack = mStacks.get(stackNdx);
+            if (isFrontStack(stack)) {
+                if (stack.mResumedActivity != null) {
+                    fgApp = stack.mResumedActivity.app;
+                } else if (stack.mPausingActivity != null) {
+                    fgApp = stack.mPausingActivity.app;
+                }
+                break;
+            }
+        }
+
+        // Now set this one as the previous process, only if that really
+        // makes sense to.
+        if (r.app != null && fgApp != null && r.app != fgApp
+                && r.lastVisibleTime > mService.mPreviousProcessVisibleTime
+                && r.app != mService.mHomeProcess) {
+            mService.mPreviousProcess = r.app;
+            mService.mPreviousProcessVisibleTime = r.lastVisibleTime;
+        }
+    }
+
     boolean resumeTopActivitiesLocked() {
         return resumeTopActivitiesLocked(null, null, null);
     }
@@ -1977,9 +2034,13 @@
         resumeTopActivitiesLocked();
     }
 
-    ActivityRecord findTaskLocked(Intent intent, ActivityInfo info) {
+    ActivityRecord findTaskLocked(ActivityRecord r) {
         for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
-            final ActivityRecord ar = mStacks.get(stackNdx).findTaskLocked(intent, info);
+            final ActivityStack stack = mStacks.get(stackNdx);
+            if (!r.isApplicationActivity() && !stack.isHomeStack()) {
+                continue;
+            }
+            final ActivityRecord ar = stack.findTaskLocked(r);
             if (ar != null) {
                 return ar;
             }
@@ -2161,21 +2222,18 @@
     }
 
     boolean switchUserLocked(int userId, UserStartedState uss) {
-        mUserStates.put(mCurrentUser, new UserState());
         mCurrentUser = userId;
-        UserState userState = mUserStates.get(userId);
-        if (userState != null) {
-            userState.restore();
-            mUserStates.delete(userId);
-        } else {
-            mFocusedStack = null;
-            if (DEBUG_STACK) Slog.d(TAG, "switchUserLocked: mStackState=" +
-                    stackStateToString(STACK_STATE_HOME_IN_FRONT));
-            mStackState = STACK_STATE_HOME_IN_FRONT;
+
+        final String homePackageName = mService.getHomePackageName();
+        if (homePackageName != null) {
+            setHomePackageName(mCurrentUser, homePackageName);
         }
 
         mStartingUsers.add(uss);
-        boolean haveActivities = mHomeStack.switchUserLocked(userId);
+        boolean haveActivities = false;
+        for (int stackNdx = mStacks.size() - 1; stackNdx >= 0; --stackNdx) {
+            haveActivities |= mStacks.get(stackNdx).switchUserLocked(userId);
+        }
 
         resumeTopActivitiesLocked();
 
@@ -2272,6 +2330,12 @@
         pw.print(prefix); pw.print("mStackState="); pw.println(stackStateToString(mStackState));
         pw.print(prefix); pw.println("mSleepTimeout: " + mSleepTimeout);
         pw.print(prefix); pw.println("mCurTaskId: " + mCurTaskId);
+        pw.print(prefix); pw.print("mHomePackageNames:");
+                for (int i = 0; i < mHomePackageNames.size(); ++i) {
+                    pw.print(" ("); pw.print(mHomePackageNames.keyAt(i)); pw.print(",");
+                    pw.print(mHomePackageNames.valueAt(i)); pw.print(")");
+                }
+                pw.println();
     }
 
     ArrayList<ActivityRecord> getDumpActivitiesLocked(String name) {
@@ -2475,8 +2539,7 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
                 case IDLE_TIMEOUT_MSG: {
-                    if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_TIMEOUT_MSG: Callers=" +
-                            Debug.getCallers(4));
+                    if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_TIMEOUT_MSG: r=" + msg.obj);
                     if (mService.mDidDexOpt) {
                         mService.mDidDexOpt = false;
                         Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG);
@@ -2489,6 +2552,7 @@
                     activityIdleInternal((ActivityRecord)msg.obj);
                 } break;
                 case IDLE_NOW_MSG: {
+                    if (DEBUG_IDLE) Slog.d(TAG, "handleMessage: IDLE_NOW_MSG: r=" + msg.obj);
                     activityIdleInternal((ActivityRecord)msg.obj);
                 } break;
                 case RESUME_TOP_ACTIVITY_MSG: {
@@ -2526,22 +2590,13 @@
         }
     }
 
-    private final class UserState {
-        final ActivityStack mSavedFocusedStack;
-        final int mSavedStackState;
+    String getHomePackageName() {
+        return mHomePackageNames.get(mCurrentUser);
+    }
 
-        public UserState() {
-            ActivityStackSupervisor supervisor = ActivityStackSupervisor.this;
-            mSavedFocusedStack = supervisor.mFocusedStack;
-            mSavedStackState = supervisor.mStackState;
-        }
-
-        void restore() {
-            ActivityStackSupervisor supervisor = ActivityStackSupervisor.this;
-            supervisor.mFocusedStack = mSavedFocusedStack;
-            if (DEBUG_STACK) Slog.d(TAG, "UserState.restore: mStackState old=" +
-                    stackStateToString(mSavedStackState));
-            supervisor.mStackState = mSavedStackState;
-        }
+    void setHomePackageName(int userId, String homePackageName) {
+        if (DEBUG_SWITCH) Slog.d(TAG, "setHomePackageName: user=" + userId + " package="
+                + homePackageName);
+        mHomePackageNames.put(userId, homePackageName);
     }
 }
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index 0e7513c..c0140e4 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -217,6 +217,7 @@
         r.receiver = app.thread.asBinder();
         r.curApp = app;
         app.curReceiver = r;
+        app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_RECEIVER);
         mService.updateLruProcessLocked(app, true);
 
         // Tell the application to launch this receiver.
@@ -230,7 +231,8 @@
             mService.ensurePackageDexOpt(r.intent.getComponent().getPackageName());
             app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver,
                     mService.compatibilityInfoForPackageLocked(r.curReceiver.applicationInfo),
-                    r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId);
+                    r.resultCode, r.resultData, r.resultExtras, r.ordered, r.userId,
+                    app.repProcState);
             if (DEBUG_BROADCAST)  Slog.v(TAG,
                     "Process cur broadcast " + r + " DELIVERED for app " + app);
             started = true;
@@ -371,7 +373,7 @@
             // If we have an app thread, do the call through that so it is
             // correctly ordered with other one-way calls.
             app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
-                    data, extras, ordered, sticky, sendingUser);
+                    data, extras, ordered, sticky, sendingUser, app.repProcState);
         } else {
             receiver.performReceive(intent, resultCode, data, extras, ordered,
                     sticky, sendingUser);
@@ -410,7 +412,7 @@
             }
         }
         if (r.appOp != AppOpsManager.OP_NONE) {
-            int mode = mService.mAppOpsService.checkOperation(r.appOp,
+            int mode = mService.mAppOpsService.noteOperation(r.appOp,
                     filter.receiverList.uid, filter.packageName);
             if (mode != AppOpsManager.MODE_ALLOWED) {
                 if (DEBUG_BROADCAST)  Slog.v(TAG,
@@ -437,7 +439,7 @@
                     // are already core system stuff so don't matter for this.
                     r.curApp = filter.receiverList.app;
                     filter.receiverList.app.curReceiver = r;
-                    mService.updateOomAdjLocked();
+                    mService.updateOomAdjLocked(r.curApp, true);
                 }
             }
             try {
@@ -717,7 +719,7 @@
                 }
             }
             if (r.appOp != AppOpsManager.OP_NONE) {
-                int mode = mService.mAppOpsService.checkOperation(r.appOp,
+                int mode = mService.mAppOpsService.noteOperation(r.appOp,
                         info.activityInfo.applicationInfo.uid, info.activityInfo.packageName);
                 if (mode != AppOpsManager.MODE_ALLOWED) {
                     if (DEBUG_BROADCAST)  Slog.v(TAG,
diff --git a/services/java/com/android/server/am/ConnectionRecord.java b/services/java/com/android/server/am/ConnectionRecord.java
index dd81493..576adc2 100644
--- a/services/java/com/android/server/am/ConnectionRecord.java
+++ b/services/java/com/android/server/am/ConnectionRecord.java
@@ -89,12 +89,15 @@
         if ((flags&Context.BIND_ADJUST_WITH_ACTIVITY) != 0) {
             sb.append("ACT ");
         }
-        if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
-            sb.append("!VIS ");
-        }
         if ((flags&Context.BIND_VISIBLE) != 0) {
             sb.append("VIS ");
         }
+        if ((flags&Context.BIND_SHOWING_UI) != 0) {
+            sb.append("UI ");
+        }
+        if ((flags&Context.BIND_NOT_VISIBLE) != 0) {
+            sb.append("!VIS ");
+        }
         if (serviceDead) {
             sb.append("DEAD ");
         }
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index f4bf9f3..d3cc56b 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -63,7 +63,7 @@
 30024 am_broadcast_discard_filter (User|1|5),(Broadcast|1|5),(Action|3),(Receiver Number|1|1),(BroadcastFilter|1|5)
 30025 am_broadcast_discard_app (User|1|5),(Broadcast|1|5),(Action|3),(Receiver Number|1|1),(App|3)
 # A service is being created
-30030 am_create_service (User|1|5),(Service Record|1|5),(Name|3),(PID|1|5)
+30030 am_create_service (User|1|5),(Service Record|1|5),(Name|3),(UID|1|5),(PID|1|5)
 # A service is being destroyed
 30031 am_destroy_service (User|1|5),(Service Record|1|5),(PID|1|5)
 # A process has crashed too many times, it is being cleared
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 5585ac8..106ba22 100644
--- a/services/java/com/android/server/am/ProcessList.java
+++ b/services/java/com/android/server/am/ProcessList.java
@@ -36,10 +36,15 @@
 
     // 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;
-    static int CACHED_APP_MIN_ADJ = 9;
+    static final int CACHED_APP_MIN_ADJ = 9;
 
     // The B list of SERVICE_ADJ -- these are the old and decrepit
     // services that aren't as shiny and interesting as the ones in the A list.
@@ -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,40 +168,17 @@
 
     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) {
         if (!mHaveDisplaySize) {
             Point p = new Point();
-            wm.getInitialDisplaySize(Display.DEFAULT_DISPLAY, p);
+            wm.getBaseDisplaySize(Display.DEFAULT_DISPLAY, p);
             if (p.x != 0 && p.y != 0) {
                 updateOomLevels(p.x, p.y, true);
                 mHaveDisplaySize = true;
@@ -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 14f4102..1b45d30 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,18 +65,17 @@
     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
     int setSchedGroup;          // Last set to background scheduling class
     int trimMemoryLevel;        // Last selected memory trimming level
     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?
@@ -90,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
@@ -123,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>();
@@ -207,24 +207,23 @@
                 pw.println(starting);
         pw.print(prefix); pw.print("lastActivityTime=");
                 TimeUtils.formatDuration(lastActivityTime, now, pw);
-                pw.print(" lruWeight="); pw.print(lruWeight);
-                pw.print(" serviceb="); pw.print(serviceb);
+                pw.print(" lruWeight="); pw.println(lruWeight);
+        pw.print(prefix); pw.print("serviceb="); pw.print(serviceb);
                 pw.print(" keeping="); pw.print(keeping);
                 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);
                 pw.print(" setSchedGroup="); pw.print(setSchedGroup);
                 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.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);
@@ -297,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) {
@@ -331,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));
             }
         }
     }
@@ -349,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;
@@ -396,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;
@@ -465,17 +484,22 @@
     }
 
     public int getSetAdjWithServices() {
-        if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ && hasStartedServices) {
-            return ProcessList.SERVICE_B_ADJ;
+        if (setAdj >= ProcessList.CACHED_APP_MIN_ADJ) {
+            if (hasStartedServices) {
+                return ProcessList.SERVICE_B_ADJ;
+            }
         }
         return setAdj;
     }
 
-    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 forceProcessStateUpTo(int newState) {
+        if (repProcState > newState) {
+            curProcState = repProcState = newState;
+        }
+    }
+
+    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/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java
index d79211c..ebcf22a 100644
--- a/services/java/com/android/server/am/TaskRecord.java
+++ b/services/java/com/android/server/am/TaskRecord.java
@@ -59,11 +59,10 @@
 
     private boolean mApplicationTask = true;
 
-    TaskRecord(int _taskId, ActivityInfo info, Intent _intent, ActivityStack _stack) {
+    TaskRecord(int _taskId, ActivityInfo info, Intent _intent) {
         taskId = _taskId;
         affinity = info.taskAffinity;
         setIntent(_intent, info);
-        stack = _stack;
     }
 
     void touchActiveTime() {
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 87263b30..22a7841 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -152,6 +152,7 @@
         IntentFilter filter = new IntentFilter();
         filter.addAction(UsbManager.ACTION_USB_STATE);
         filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
         mContext.registerReceiver(mStateReceiver, filter);
 
         filter = new IntentFilter();
@@ -511,6 +512,8 @@
                     if (VDBG) Log.d(TAG, "Tethering got CONNECTIVITY_ACTION");
                     mTetherMasterSM.sendMessage(TetherMasterSM.CMD_UPSTREAM_CHANGED);
                 }
+            } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+                updateConfiguration();
             }
         }
     }
@@ -613,7 +616,7 @@
     public int[] getUpstreamIfaceTypes() {
         int values[];
         synchronized (mPublicSync) {
-            updateConfiguration();
+            updateConfiguration();  // TODO - remove?
             values = new int[mUpstreamIfaceTypes.size()];
             Iterator<Integer> iterator = mUpstreamIfaceTypes.iterator();
             for (int i=0; i < mUpstreamIfaceTypes.size(); i++) {
@@ -1284,7 +1287,7 @@
                 int upType = ConnectivityManager.TYPE_NONE;
                 String iface = null;
 
-                updateConfiguration();
+                updateConfiguration(); // TODO - remove?
 
                 synchronized (mPublicSync) {
                     if (VDBG) {
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index 2fc972f..3a2391f 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -18,6 +18,7 @@
 
 import static android.Manifest.permission.BIND_VPN_SERVICE;
 
+import android.app.AppGlobals;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
@@ -28,8 +29,10 @@
 import android.content.IntentFilter;
 import android.content.ServiceConnection;
 import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
+import android.content.pm.UserInfo;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
@@ -37,6 +40,7 @@
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.INetworkManagementEventObserver;
+import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.LocalSocket;
 import android.net.LocalSocketAddress;
@@ -54,11 +58,14 @@
 import android.os.SystemClock;
 import android.os.SystemService;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.security.Credentials;
 import android.security.KeyStore;
 import android.util.Log;
+import android.util.SparseBooleanArray;
 import android.widget.Toast;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.internal.R;
 import com.android.internal.net.LegacyVpnInfo;
 import com.android.internal.net.VpnConfig;
@@ -74,6 +81,7 @@
 import java.net.InetAddress;
 import java.nio.charset.StandardCharsets;
 import java.util.Arrays;
+import java.util.List;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import libcore.io.IoUtils;
@@ -98,20 +106,53 @@
     private volatile boolean mEnableNotif = true;
     private volatile boolean mEnableTeardown = true;
     private final IConnectivityManager mConnService;
+    private VpnConfig mConfig;
+
+    /* list of users using this VPN. */
+    @GuardedBy("this")
+    private SparseBooleanArray mVpnUsers = null;
+    private BroadcastReceiver mUserIntentReceiver = null;
+
+    private final int mUserId;
 
     public Vpn(Context context, VpnCallback callback, INetworkManagementService netService,
-            IConnectivityManager connService) {
+            IConnectivityManager connService, int userId) {
         // TODO: create dedicated TYPE_VPN network type
         super(ConnectivityManager.TYPE_DUMMY);
         mContext = context;
         mCallback = callback;
         mConnService = connService;
+        mUserId = userId;
 
         try {
             netService.registerObserver(mObserver);
         } catch (RemoteException e) {
             Log.wtf(TAG, "Problem registering observer", e);
         }
+        if (userId == UserHandle.USER_OWNER) {
+            // Owner's VPN also needs to handle restricted users
+            mUserIntentReceiver = new BroadcastReceiver() {
+                @Override
+                public void onReceive(Context context, Intent intent) {
+                    final String action = intent.getAction();
+                    final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+                            UserHandle.USER_NULL);
+                    if (userId == UserHandle.USER_NULL) return;
+
+                    if (Intent.ACTION_USER_ADDED.equals(action)) {
+                        onUserAdded(userId);
+                    } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
+                        onUserRemoved(userId);
+                    }
+                }
+            };
+
+            IntentFilter intentFilter = new IntentFilter();
+            intentFilter.addAction(Intent.ACTION_USER_ADDED);
+            intentFilter.addAction(Intent.ACTION_USER_REMOVED);
+            mContext.registerReceiverAsUser(
+                    mUserIntentReceiver, UserHandle.ALL, intentFilter, null, null);
+        }
     }
 
     /**
@@ -197,15 +238,23 @@
 
         // Reset the interface and hide the notification.
         if (mInterface != null) {
-            jniReset(mInterface);
             final long token = Binder.clearCallingIdentity();
             try {
                 mCallback.restore();
-                hideNotification();
+                final int size = mVpnUsers.size();
+                for (int i = 0; i < size; i++) {
+                    int user = mVpnUsers.keyAt(i);
+                    mCallback.clearUserForwarding(mInterface, user);
+                    hideNotification(user);
+                }
+
+                mCallback.clearMarkedForwarding(mInterface);
             } finally {
                 Binder.restoreCallingIdentity(token);
             }
+            jniReset(mInterface);
             mInterface = null;
+            mVpnUsers = null;
         }
 
         // Revoke the connection or stop LegacyVpnRunner.
@@ -225,6 +274,7 @@
 
         Log.i(TAG, "Switched from " + mPackage + " to " + newPackage);
         mPackage = newPackage;
+        mConfig = null;
         updateState(DetailedState.IDLE, "prepare");
         return true;
     }
@@ -237,12 +287,22 @@
      * @param interfaze The name of the interface.
      */
     public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception {
+
         PackageManager pm = mContext.getPackageManager();
-        ApplicationInfo app = pm.getApplicationInfo(mPackage, 0);
-        if (Binder.getCallingUid() != app.uid) {
+        int appUid = pm.getPackageUid(mPackage, mUserId);
+        if (Binder.getCallingUid() != appUid) {
             throw new SecurityException("Unauthorized Caller");
         }
+        // protect the socket from routing rules
+        final long token = Binder.clearCallingIdentity();
+        try {
+            mCallback.protect(socket);
+        } finally {
+            Binder.restoreCallingIdentity(token);
+        }
+        // bind the socket to the interface
         jniProtect(socket.getFd(), interfaze);
+
     }
 
     /**
@@ -255,44 +315,40 @@
      */
     public synchronized ParcelFileDescriptor establish(VpnConfig config) {
         // Check if the caller is already prepared.
+        UserManager mgr = UserManager.get(mContext);
         PackageManager pm = mContext.getPackageManager();
         ApplicationInfo app = null;
         try {
-            app = pm.getApplicationInfo(mPackage, 0);
+            app = AppGlobals.getPackageManager().getApplicationInfo(mPackage, 0, mUserId);
+            if (Binder.getCallingUid() != app.uid) {
+                return null;
+            }
         } catch (Exception e) {
             return null;
         }
-        if (Binder.getCallingUid() != app.uid) {
-            return null;
-        }
-
         // Check if the service is properly declared.
         Intent intent = new Intent(VpnConfig.SERVICE_INTERFACE);
         intent.setClassName(mPackage, config.user);
-        ResolveInfo info = pm.resolveService(intent, 0);
-        if (info == null) {
-            throw new SecurityException("Cannot find " + config.user);
-        }
-        if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
-            throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
-        }
+        long token = Binder.clearCallingIdentity();
+        try {
+            // Restricted users are not allowed to create VPNs, they are tied to Owner
+            UserInfo user = mgr.getUserInfo(mUserId);
+            if (user.isRestricted()) {
+                throw new SecurityException("Restricted users cannot establish VPNs");
+            }
 
-        // Load the label.
-        String label = app.loadLabel(pm).toString();
-
-        // Load the icon and convert it into a bitmap.
-        Drawable icon = app.loadIcon(pm);
-        Bitmap bitmap = null;
-        if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
-            int width = mContext.getResources().getDimensionPixelSize(
-                    android.R.dimen.notification_large_icon_width);
-            int height = mContext.getResources().getDimensionPixelSize(
-                    android.R.dimen.notification_large_icon_height);
-            icon.setBounds(0, 0, width, height);
-            bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
-            Canvas c = new Canvas(bitmap);
-            icon.draw(c);
-            c.setBitmap(null);
+            ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
+                                                                        null, 0, mUserId);
+            if (info == null) {
+                throw new SecurityException("Cannot find " + config.user);
+            }
+            if (!BIND_VPN_SERVICE.equals(info.serviceInfo.permission)) {
+                throw new SecurityException(config.user + " does not require " + BIND_VPN_SERVICE);
+            }
+        } catch (RemoteException e) {
+                throw new SecurityException("Cannot find " + config.user);
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
 
         // Configure the interface. Abort if any of these steps fails.
@@ -300,14 +356,18 @@
         try {
             updateState(DetailedState.CONNECTING, "establish");
             String interfaze = jniGetName(tun.getFd());
-            if (jniSetAddresses(interfaze, config.addresses) < 1) {
+
+            // TEMP use the old jni calls until there is support for netd address setting
+            StringBuilder builder = new StringBuilder();
+            for (LinkAddress address : config.addresses) {
+                builder.append(" " + address);
+            }
+            if (jniSetAddresses(interfaze, builder.toString()) < 1) {
                 throw new IllegalArgumentException("At least one address must be specified");
             }
-            if (config.routes != null) {
-                jniSetRoutes(interfaze, config.routes);
-            }
             Connection connection = new Connection();
-            if (!mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE)) {
+            if (!mContext.bindServiceAsUser(intent, connection, Context.BIND_AUTO_CREATE,
+                        new UserHandle(mUserId))) {
                 throw new IllegalStateException("Cannot bind " + config.user);
             }
             if (mConnection != null) {
@@ -318,30 +378,147 @@
             }
             mConnection = connection;
             mInterface = interfaze;
+
+            // Fill more values.
+            config.user = mPackage;
+            config.interfaze = mInterface;
+            config.startTime = SystemClock.elapsedRealtime();
+            mConfig = config;
+            // Set up forwarding and DNS rules.
+            mVpnUsers = new SparseBooleanArray();
+            token = Binder.clearCallingIdentity();
+            try {
+                mCallback.setMarkedForwarding(mInterface);
+                mCallback.setRoutes(interfaze, config.routes);
+                mCallback.override(mInterface, config.dnsServers, config.searchDomains);
+                addVpnUserLocked(mUserId);
+
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
+
         } catch (RuntimeException e) {
             updateState(DetailedState.FAILED, "establish");
             IoUtils.closeQuietly(tun);
+            // make sure marked forwarding is cleared if it was set
+            try {
+                mCallback.clearMarkedForwarding(mInterface);
+            } catch (Exception ingored) {
+                // ignored
+            }
             throw e;
         }
         Log.i(TAG, "Established by " + config.user + " on " + mInterface);
 
-        // Fill more values.
-        config.user = mPackage;
-        config.interfaze = mInterface;
 
-        // Override DNS servers and show the notification.
-        final long token = Binder.clearCallingIdentity();
-        try {
-            mCallback.override(config.dnsServers, config.searchDomains);
-            showNotification(config, label, bitmap);
-        } finally {
-            Binder.restoreCallingIdentity(token);
+        // If we are owner assign all Restricted Users to this VPN
+        if (mUserId == UserHandle.USER_OWNER) {
+            token = Binder.clearCallingIdentity();
+            try {
+                for (UserInfo user : mgr.getUsers()) {
+                    if (user.isRestricted()) {
+                        try {
+                            addVpnUserLocked(user.id);
+                        } catch (Exception e) {
+                            Log.wtf(TAG, "Failed to add user " + user.id + " to owner's VPN");
+                        }
+                    }
+                }
+            } finally {
+                Binder.restoreCallingIdentity(token);
+            }
         }
         // TODO: ensure that contract class eventually marks as connected
         updateState(DetailedState.AUTHENTICATING, "establish");
         return tun;
     }
 
+    private boolean isRunningLocked() {
+        return mVpnUsers != null;
+    }
+
+    private void addVpnUserLocked(int user) {
+        enforceControlPermission();
+
+        if (!isRunningLocked()) {
+            throw new IllegalStateException("VPN is not active");
+        }
+        // add the user
+        mCallback.addUserForwarding(mInterface, user);
+        mVpnUsers.put(user, true);
+
+        // show the notification
+        if (!mPackage.equals(VpnConfig.LEGACY_VPN)) {
+            // Load everything for the user's notification
+            PackageManager pm = mContext.getPackageManager();
+            ApplicationInfo app = null;
+            try {
+                app = AppGlobals.getPackageManager().getApplicationInfo(mPackage, 0, mUserId);
+            } catch (RemoteException e) {
+                throw new IllegalStateException("Invalid application");
+            }
+            String label = app.loadLabel(pm).toString();
+            // Load the icon and convert it into a bitmap.
+            Drawable icon = app.loadIcon(pm);
+            Bitmap bitmap = null;
+            if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
+                int width = mContext.getResources().getDimensionPixelSize(
+                        android.R.dimen.notification_large_icon_width);
+                int height = mContext.getResources().getDimensionPixelSize(
+                        android.R.dimen.notification_large_icon_height);
+                icon.setBounds(0, 0, width, height);
+                bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+                Canvas c = new Canvas(bitmap);
+                icon.draw(c);
+                c.setBitmap(null);
+            }
+            showNotification(label, bitmap, user);
+        } else {
+            showNotification(null, null, user);
+        }
+    }
+
+    private void removeVpnUserLocked(int user) {
+            enforceControlPermission();
+
+            if (!isRunningLocked()) {
+                throw new IllegalStateException("VPN is not active");
+            }
+            mCallback.clearUserForwarding(mInterface, user);
+            mVpnUsers.delete(user);
+            hideNotification(user);
+    }
+
+    private void onUserAdded(int userId) {
+        // If the user is restricted tie them to the owner's VPN
+        synchronized(Vpn.this) {
+            UserManager mgr = UserManager.get(mContext);
+            UserInfo user = mgr.getUserInfo(userId);
+            if (user.isRestricted()) {
+                try {
+                    addVpnUserLocked(userId);
+                } catch (Exception e) {
+                    Log.wtf(TAG, "Failed to add restricted user to owner", e);
+                }
+            }
+        }
+    }
+
+    private void onUserRemoved(int userId) {
+        // clean up if restricted
+        synchronized(Vpn.this) {
+            UserManager mgr = UserManager.get(mContext);
+            UserInfo user = mgr.getUserInfo(userId);
+            if (user.isRestricted()) {
+                try {
+                    removeVpnUserLocked(userId);
+                } catch (Exception e) {
+                    Log.wtf(TAG, "Failed to remove restricted user to owner", e);
+                }
+            }
+        }
+    }
+
     @Deprecated
     public synchronized void interfaceStatusChanged(String iface, boolean up) {
         try {
@@ -367,8 +544,16 @@
                 if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
                     final long token = Binder.clearCallingIdentity();
                     try {
+                        final int size = mVpnUsers.size();
+                        for (int i = 0; i < size; i++) {
+                            int user = mVpnUsers.keyAt(i);
+                            mCallback.clearUserForwarding(mInterface, user);
+                            hideNotification(user);
+                        }
+                        mVpnUsers = null;
+                        mCallback.clearMarkedForwarding(mInterface);
+
                         mCallback.restore();
-                        hideNotification();
                     } finally {
                         Binder.restoreCallingIdentity(token);
                     }
@@ -391,16 +576,19 @@
         if (Binder.getCallingUid() == Process.SYSTEM_UID) {
             return;
         }
-
+        int appId = UserHandle.getAppId(Binder.getCallingUid());
+        final long token = Binder.clearCallingIdentity();
         try {
             // System dialogs are also allowed to control VPN.
             PackageManager pm = mContext.getPackageManager();
             ApplicationInfo app = pm.getApplicationInfo(VpnConfig.DIALOGS_PACKAGE, 0);
-            if (Binder.getCallingUid() == app.uid) {
+            if (appId == app.uid) {
                 return;
             }
         } catch (Exception e) {
             // ignore
+        } finally {
+            Binder.restoreCallingIdentity(token);
         }
 
         throw new SecurityException("Unauthorized Caller");
@@ -420,9 +608,9 @@
         }
     }
 
-    private void showNotification(VpnConfig config, String label, Bitmap icon) {
+    private void showNotification(String label, Bitmap icon, int user) {
         if (!mEnableNotif) return;
-        mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, config);
+        mStatusIntent = VpnConfig.getIntentForStatusPanel(mContext, mConfig);
 
         NotificationManager nm = (NotificationManager)
                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
@@ -430,9 +618,8 @@
         if (nm != null) {
             String title = (label == null) ? mContext.getString(R.string.vpn_title) :
                     mContext.getString(R.string.vpn_title_long, label);
-            String text = (config.session == null) ? mContext.getString(R.string.vpn_text) :
-                    mContext.getString(R.string.vpn_text_long, config.session);
-            config.startTime = SystemClock.elapsedRealtime();
+            String text = (mConfig.session == null) ? mContext.getString(R.string.vpn_text) :
+                    mContext.getString(R.string.vpn_text_long, mConfig.session);
 
             Notification notification = new Notification.Builder(mContext)
                     .setSmallIcon(R.drawable.vpn_connected)
@@ -443,11 +630,11 @@
                     .setDefaults(0)
                     .setOngoing(true)
                     .build();
-            nm.notifyAsUser(null, R.drawable.vpn_connected, notification, UserHandle.ALL);
+            nm.notifyAsUser(null, R.drawable.vpn_connected, notification, new UserHandle(user));
         }
     }
 
-    private void hideNotification() {
+    private void hideNotification(int user) {
         if (!mEnableNotif) return;
         mStatusIntent = null;
 
@@ -455,7 +642,7 @@
                 mContext.getSystemService(Context.NOTIFICATION_SERVICE);
 
         if (nm != null) {
-            nm.cancelAsUser(null, R.drawable.vpn_connected, UserHandle.ALL);
+            nm.cancelAsUser(null, R.drawable.vpn_connected, new UserHandle(user));
         }
     }
 
@@ -467,15 +654,15 @@
     private native int jniCheck(String interfaze);
     private native void jniProtect(int socket, String interfaze);
 
-    private static String findLegacyVpnGateway(LinkProperties prop) {
-        for (RouteInfo route : prop.getRoutes()) {
+    private static RouteInfo findIPv4DefaultRoute(LinkProperties prop) {
+        for (RouteInfo route : prop.getAllRoutes()) {
             // Currently legacy VPN only works on IPv4.
             if (route.isDefaultRoute() && route.getGateway() instanceof Inet4Address) {
-                return route.getGateway().getHostAddress();
+                return route;
             }
         }
 
-        throw new IllegalStateException("Unable to find suitable gateway");
+        throw new IllegalStateException("Unable to find IPv4 default gateway");
     }
 
     /**
@@ -488,8 +675,9 @@
             throw new IllegalStateException("KeyStore isn't unlocked");
         }
 
-        final String iface = egress.getInterfaceName();
-        final String gateway = findLegacyVpnGateway(egress);
+        final RouteInfo ipv4DefaultRoute = findIPv4DefaultRoute(egress);
+        final String gateway = ipv4DefaultRoute.getGateway().getHostAddress();
+        final String iface = ipv4DefaultRoute.getInterface();
 
         // Load certificates.
         String privateKey = "";
@@ -576,7 +764,8 @@
         config.user = profile.key;
         config.interfaze = iface;
         config.session = profile.name;
-        config.routes = profile.routes;
+
+        config.addLegacyRoutes(profile.routes);
         if (!profile.dnsServers.isEmpty()) {
             config.dnsServers = Arrays.asList(profile.dnsServers.split(" +"));
         }
@@ -619,7 +808,7 @@
         if (mLegacyVpnRunner == null) return null;
 
         final LegacyVpnInfo info = new LegacyVpnInfo();
-        info.key = mLegacyVpnRunner.mConfig.user;
+        info.key = mConfig.user;
         info.state = LegacyVpnInfo.stateFromNetworkInfo(mNetworkInfo);
         if (mNetworkInfo.isConnected()) {
             info.intent = mStatusIntent;
@@ -629,7 +818,7 @@
 
     public VpnConfig getLegacyVpnConfig() {
         if (mLegacyVpnRunner != null) {
-            return mLegacyVpnRunner.mConfig;
+            return mConfig;
         } else {
             return null;
         }
@@ -645,7 +834,6 @@
     private class LegacyVpnRunner extends Thread {
         private static final String TAG = "LegacyVpnRunner";
 
-        private final VpnConfig mConfig;
         private final String[] mDaemons;
         private final String[][] mArguments;
         private final LocalSocket[] mSockets;
@@ -690,7 +878,7 @@
             // mConfig.interfaze will change to point to OUR
             // internal interface soon. TODO - add inner/outer to mconfig
             // TODO - we have a race - if the outer iface goes away/disconnects before we hit this
-            // we will leave the VPN up.  We should check that it's still there/connected after 
+            // we will leave the VPN up.  We should check that it's still there/connected after
             // registering
             mOuterInterface = mConfig.interfaze;
 
@@ -866,11 +1054,11 @@
 
                 // Set the interface and the addresses in the config.
                 mConfig.interfaze = parameters[0].trim();
-                mConfig.addresses = parameters[1].trim();
 
+                mConfig.addLegacyAddresses(parameters[1]);
                 // Set the routes if they are not set in the config.
                 if (mConfig.routes == null || mConfig.routes.isEmpty()) {
-                    mConfig.routes = parameters[2].trim();
+                    mConfig.addLegacyRoutes(parameters[2]);
                 }
 
                 // Set the DNS servers if they are not set in the config.
@@ -890,7 +1078,13 @@
                 }
 
                 // Set the routes.
-                jniSetRoutes(mConfig.interfaze, mConfig.routes);
+                long token = Binder.clearCallingIdentity();
+                try {
+                    mCallback.setMarkedForwarding(mConfig.interfaze);
+                    mCallback.setRoutes(mConfig.interfaze, mConfig.routes);
+                } finally {
+                    Binder.restoreCallingIdentity(token);
+                }
 
                 // Here is the last step and it must be done synchronously.
                 synchronized (Vpn.this) {
@@ -904,14 +1098,44 @@
 
                     // Now INetworkManagementEventObserver is watching our back.
                     mInterface = mConfig.interfaze;
-                    mCallback.override(mConfig.dnsServers, mConfig.searchDomains);
-                    showNotification(mConfig, null, null);
+                    mVpnUsers = new SparseBooleanArray();
 
+                    token = Binder.clearCallingIdentity();
+                    try {
+                        mCallback.override(mInterface, mConfig.dnsServers, mConfig.searchDomains);
+                        addVpnUserLocked(mUserId);
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
+
+                    // Assign all restircted users to this VPN
+                    // (Legacy VPNs are Owner only)
+                    UserManager mgr = UserManager.get(mContext);
+                    token = Binder.clearCallingIdentity();
+                    try {
+                        for (UserInfo user : mgr.getUsers()) {
+                            if (user.isRestricted()) {
+                                try {
+                                    addVpnUserLocked(user.id);
+                                } catch (Exception e) {
+                                    Log.wtf(TAG, "Failed to add user " + user.id
+                                            + " to owner's VPN");
+                                }
+                            }
+                        }
+                    } finally {
+                        Binder.restoreCallingIdentity(token);
+                    }
                     Log.i(TAG, "Connected!");
                     updateState(DetailedState.CONNECTED, "execute");
                 }
             } catch (Exception e) {
                 Log.i(TAG, "Aborting", e);
+                // make sure the routing is cleared
+                try {
+                    mCallback.clearMarkedForwarding(mConfig.interfaze);
+                } catch (Exception ignored) {
+                }
                 exit();
             } finally {
                 // Kill the daemons if they fail to stop.
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/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 4791ec0..38453c8 100644
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -892,7 +892,8 @@
             for (int i=0; i<newWork.size(); i++) {
                 try {
                     int uid = newWork.get(i);
-                    mAppOpsService.startOperation(AppOpsManager.OP_GPS, uid, newWork.getName(i));
+                    mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
+                            AppOpsManager.OP_GPS, uid, newWork.getName(i));
                     if (uid != lastuid) {
                         lastuid = uid;
                         mBatteryStats.noteStartGps(uid);
@@ -909,7 +910,8 @@
             for (int i=0; i<goneWork.size(); i++) {
                 try {
                     int uid = goneWork.get(i);
-                    mAppOpsService.finishOperation(AppOpsManager.OP_GPS, uid, goneWork.getName(i));
+                    mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
+                            AppOpsManager.OP_GPS, uid, goneWork.getName(i));
                     if (uid != lastuid) {
                         lastuid = uid;
                         mBatteryStats.noteStopGps(uid);
diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java
index 13e400f..a2e9d676 100644
--- a/services/java/com/android/server/net/LockdownVpnTracker.java
+++ b/services/java/com/android/server/net/LockdownVpnTracker.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.net.LinkProperties;
+import android.net.LinkAddress;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
 import android.net.NetworkInfo.State;
@@ -44,6 +45,8 @@
 import com.android.server.EventLogTags;
 import com.android.server.connectivity.Vpn;
 
+import java.util.List;
+
 /**
  * State tracker for lockdown mode. Watches for normal {@link NetworkInfo} to be
  * connected and kicks off VPN connection, managing any required {@code netd}
@@ -73,7 +76,7 @@
 
     private String mAcceptedEgressIface;
     private String mAcceptedIface;
-    private String mAcceptedSourceAddr;
+    private List<LinkAddress> mAcceptedSourceAddr;
 
     private int mErrorCount;
 
@@ -162,14 +165,15 @@
 
         } else if (vpnInfo.isConnected() && vpnConfig != null) {
             final String iface = vpnConfig.interfaze;
-            final String sourceAddr = vpnConfig.addresses;
+            final List<LinkAddress> sourceAddrs = vpnConfig.addresses;
 
             if (TextUtils.equals(iface, mAcceptedIface)
-                    && TextUtils.equals(sourceAddr, mAcceptedSourceAddr)) {
+                  && sourceAddrs.equals(mAcceptedSourceAddr)) {
                 return;
             }
 
-            Slog.d(TAG, "VPN connected using iface=" + iface + ", sourceAddr=" + sourceAddr);
+            Slog.d(TAG, "VPN connected using iface=" + iface +
+                    ", sourceAddr=" + sourceAddrs.toString());
             EventLogTags.writeLockdownVpnConnected(egressType);
             showNotification(R.string.vpn_lockdown_connected, R.drawable.vpn_connected);
 
@@ -177,11 +181,13 @@
                 clearSourceRulesLocked();
 
                 mNetService.setFirewallInterfaceRule(iface, true);
-                mNetService.setFirewallEgressSourceRule(sourceAddr, true);
+                for (LinkAddress addr : sourceAddrs) {
+                    mNetService.setFirewallEgressSourceRule(addr.toString(), true);
+                }
 
                 mErrorCount = 0;
                 mAcceptedIface = iface;
-                mAcceptedSourceAddr = sourceAddr;
+                mAcceptedSourceAddr = sourceAddrs;
             } catch (RemoteException e) {
                 throw new RuntimeException("Problem setting firewall rules", e);
             }
@@ -263,7 +269,9 @@
                 mAcceptedIface = null;
             }
             if (mAcceptedSourceAddr != null) {
-                mNetService.setFirewallEgressSourceRule(mAcceptedSourceAddr, false);
+                for (LinkAddress addr : mAcceptedSourceAddr) {
+                    mNetService.setFirewallEgressSourceRule(addr.toString(), false);
+                }
                 mAcceptedSourceAddr = null;
             }
         } catch (RemoteException e) {
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 5074409..05eeb36 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -154,7 +154,7 @@
 
     private final Context mContext;
     private final INetworkManagementService mNetworkManager;
-    private final IAlarmManager mAlarmManager;
+    private final AlarmManager mAlarmManager;
     private final TrustedTime mTime;
     private final TelephonyManager mTeleManager;
     private final NetworkStatsSettings mSettings;
@@ -261,10 +261,10 @@
             NetworkStatsSettings settings) {
         mContext = checkNotNull(context, "missing Context");
         mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService");
-        mAlarmManager = checkNotNull(alarmManager, "missing IAlarmManager");
         mTime = checkNotNull(time, "missing TrustedTime");
         mTeleManager = checkNotNull(TelephonyManager.getDefault(), "missing TelephonyManager");
         mSettings = checkNotNull(settings, "missing NetworkStatsSettings");
+        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
 
         final PowerManager powerManager = (PowerManager) context.getSystemService(
                 Context.POWER_SERVICE);
@@ -420,20 +420,16 @@
      * reschedule based on current {@link NetworkStatsSettings#getPollInterval()}.
      */
     private void registerPollAlarmLocked() {
-        try {
-            if (mPollIntent != null) {
-                mAlarmManager.remove(mPollIntent);
-            }
-
-            mPollIntent = PendingIntent.getBroadcast(
-                    mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0);
-
-            final long currentRealtime = SystemClock.elapsedRealtime();
-            mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
-                    mSettings.getPollInterval(), mPollIntent);
-        } catch (RemoteException e) {
-            // ignored; service lives in system_server
+        if (mPollIntent != null) {
+            mAlarmManager.cancel(mPollIntent);
         }
+
+        mPollIntent = PendingIntent.getBroadcast(
+                mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0);
+
+        final long currentRealtime = SystemClock.elapsedRealtime();
+        mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
+                mSettings.getPollInterval(), mPollIntent);
     }
 
     /**
diff --git a/services/java/com/android/server/pm/KeySetManager.java b/services/java/com/android/server/pm/KeySetManager.java
index 3480b19..93992c2 100644
--- a/services/java/com/android/server/pm/KeySetManager.java
+++ b/services/java/com/android/server/pm/KeySetManager.java
@@ -69,7 +69,7 @@
         mPackages = packages;
     }
 
-    /*
+    /**
      * Determine if a package is signed by the given KeySet.
      *
      * Returns false if the package was not signed by all the
@@ -94,7 +94,7 @@
         }
     }
 
-    /*
+    /**
      * This informs the system that the given package has defined a KeySet
      * in its manifest that a) contains the given keys and b) is named
      * alias by that package.
@@ -116,7 +116,7 @@
         }
     }
 
-    /*
+    /**
      * Similar to the above, this informs the system that the given package
      * was signed by the provided KeySet.
      */
@@ -153,10 +153,9 @@
         }
     }
 
-    /*
-     * Fetches the stable identifier associated with the given KeySet.
-     *
-     * Returns KEYSET_NOT_FOUND if the KeySet... wasn't found.
+    /**
+     * Fetches the stable identifier associated with the given KeySet. Returns
+     * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found.
      */
     public long getIdByKeySet(KeySet ks) {
         synchronized (mLockObject) {
@@ -174,10 +173,11 @@
         return KEYSET_NOT_FOUND;
     }
 
-    /*
+    /**
      * Fetches the KeySet corresponding to the given stable identifier.
      *
-     * Returns KEYSET_NOT_FOUND if the identifier doesn't identify a KeySet.
+     * Returns {@link #KEYSET_NOT_FOUND} if the identifier doesn't
+     * identify a {@link KeySet}.
      */
     public KeySet getKeySetById(long id) {
         synchronized (mLockObject) {
@@ -185,7 +185,7 @@
         }
     }
 
-    /*
+    /**
      * Fetches the KeySet that a given package refers to by the provided alias.
      *
      * If the package isn't known to us, throws an IllegalArgumentException.
@@ -205,10 +205,9 @@
         }
     }
 
-    /*
-     * Fetches all the known KeySets that signed the given package.
-     *
-     * If the package is unknown to us, throws an IllegalArgumentException.
+    /**
+     * Fetches all the known {@link KeySet KeySets} that signed the given
+     * package. Returns {@code null} if package is unknown.
      */
     public Set<KeySet> getSigningKeySetsByPackageName(String packageName) {
         synchronized (mLockObject) {
@@ -227,16 +226,16 @@
         }
     }
 
-    /*
+    /**
      * Creates a new KeySet corresponding to the given keys.
      *
-     * If the PublicKeys aren't known to the system, this adds them. Otherwise,
-     * they're deduped.
+     * If the {@link PublicKey PublicKeys} aren't known to the system, this
+     * adds them. Otherwise, they're deduped.
      *
      * If the KeySet isn't known to the system, this adds that and creates the
      * mapping to the PublicKeys. If it is known, then it's deduped.
      *
-     * Throws if the provided set is null.
+     * Throws if the provided set is {@code null}.
      */
     private KeySet addKeySetLocked(Set<PublicKey> keys) {
         if (keys == null) {
@@ -267,7 +266,7 @@
         return ks;
     }
 
-    /*
+    /**
      * Adds the given PublicKey to the system, deduping as it goes.
      */
     private long addPublicKeyLocked(PublicKey key) {
@@ -284,7 +283,7 @@
         return id;
     }
 
-    /*
+    /**
      * Finds the stable identifier for a KeySet based on a set of PublicKey stable IDs.
      *
      * Returns KEYSET_NOT_FOUND if there isn't one.
@@ -299,7 +298,7 @@
         return KEYSET_NOT_FOUND;
     }
 
-    /*
+    /**
      * Finds the stable identifier for a PublicKey or PUBLIC_KEY_NOT_FOUND.
      */
     private long getIdForPublicKeyLocked(PublicKey k) {
@@ -314,7 +313,7 @@
         return PUBLIC_KEY_NOT_FOUND;
     }
 
-    /*
+    /**
      * Gets an unused stable identifier for a KeySet.
      */
     private long getFreeKeySetIDLocked() {
@@ -322,7 +321,7 @@
         return lastIssuedKeySetId;
     }
 
-    /*
+    /**
      * Same as above, but for public keys.
      */
     private long getFreePublicKeyIdLocked() {
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 8fde010..7a01219 100755
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -1246,7 +1246,7 @@
 
             // Find base frameworks (resource packages without code).
             mFrameworkInstallObserver = new AppDirObserver(
-                mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
+                mFrameworkDir.getPath(), OBSERVER_EVENTS, true, false);
             mFrameworkInstallObserver.startWatching();
             scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR,
@@ -1255,7 +1255,7 @@
             // Collected privileged system packages.
             mPrivilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
             mPrivilegedInstallObserver = new AppDirObserver(
-                    mPrivilegedAppDir.getPath(), OBSERVER_EVENTS, true);
+                    mPrivilegedAppDir.getPath(), OBSERVER_EVENTS, true, true);
             mPrivilegedInstallObserver.startWatching();
                 scanDirLI(mPrivilegedAppDir, PackageParser.PARSE_IS_SYSTEM
                         | PackageParser.PARSE_IS_SYSTEM_DIR
@@ -1264,7 +1264,7 @@
             // Collect ordinary system packages.
             mSystemAppDir = new File(Environment.getRootDirectory(), "app");
             mSystemInstallObserver = new AppDirObserver(
-                mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
+                mSystemAppDir.getPath(), OBSERVER_EVENTS, true, false);
             mSystemInstallObserver.startWatching();
             scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
@@ -1272,7 +1272,7 @@
             // Collect all vendor packages.
             mVendorAppDir = new File("/vendor/app");
             mVendorInstallObserver = new AppDirObserver(
-                mVendorAppDir.getPath(), OBSERVER_EVENTS, true);
+                mVendorAppDir.getPath(), OBSERVER_EVENTS, true, false);
             mVendorInstallObserver.startWatching();
             scanDirLI(mVendorAppDir, PackageParser.PARSE_IS_SYSTEM
                     | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
@@ -1345,12 +1345,12 @@
                 EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                         SystemClock.uptimeMillis());
                 mAppInstallObserver = new AppDirObserver(
-                    mAppInstallDir.getPath(), OBSERVER_EVENTS, false);
+                    mAppInstallDir.getPath(), OBSERVER_EVENTS, false, false);
                 mAppInstallObserver.startWatching();
                 scanDirLI(mAppInstallDir, 0, scanMode, 0);
     
                 mDrmAppInstallObserver = new AppDirObserver(
-                    mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false);
+                    mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false, false);
                 mDrmAppInstallObserver.startWatching();
                 scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
                         scanMode, 0);
@@ -5972,10 +5972,11 @@
     }
     
     private final class AppDirObserver extends FileObserver {
-        public AppDirObserver(String path, int mask, boolean isrom) {
+        public AppDirObserver(String path, int mask, boolean isrom, boolean isPrivileged) {
             super(path, mask);
             mRootDir = path;
             mIsRom = isrom;
+            mIsPrivileged = isPrivileged;
         }
 
         public void onEvent(int event, String path) {
@@ -6036,11 +6037,15 @@
                 if ((event&ADD_EVENTS) != 0) {
                     if (p == null) {
                         if (DEBUG_INSTALL) Slog.d(TAG, "New file appeared: " + fullPath);
-                        p = scanPackageLI(fullPath,
-                                (mIsRom ? PackageParser.PARSE_IS_SYSTEM
-                                        | PackageParser.PARSE_IS_SYSTEM_DIR: 0) |
-                                PackageParser.PARSE_CHATTY |
-                                PackageParser.PARSE_MUST_BE_APK,
+                        int flags = PackageParser.PARSE_CHATTY | PackageParser.PARSE_MUST_BE_APK;
+                        if (mIsRom) {
+                            flags |= PackageParser.PARSE_IS_SYSTEM
+                                    | PackageParser.PARSE_IS_SYSTEM_DIR;
+                            if (mIsPrivileged) {
+                                flags |= PackageParser.PARSE_IS_PRIVILEGED;
+                            }
+                        }
+                        p = scanPackageLI(fullPath, flags,
                                 SCAN_MONITOR | SCAN_NO_PATHS | SCAN_UPDATE_TIME,
                                 System.currentTimeMillis(), UserHandle.ALL);
                         if (p != null) {
@@ -6083,6 +6088,7 @@
 
         private final String mRootDir;
         private final boolean mIsRom;
+        private final boolean mIsPrivileged;
     }
 
     /* Called when a downloaded package installation has been confirmed by the user */
@@ -6183,9 +6189,9 @@
         PackageSetting pkgSetting;
         final int uid = Binder.getCallingUid();
         if (UserHandle.getUserId(uid) != userId) {
-            mContext.enforceCallingPermission(
+            mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
-                    "setApplicationBlocked for user " + userId);
+                    "setApplicationBlockedSetting for user " + userId);
         }
 
         if (blocked && isPackageDeviceAdmin(packageName, userId)) {
@@ -6218,6 +6224,8 @@
                 return true;
             }
             if (sendRemoved) {
+                killApplication(packageName, UserHandle.getUid(userId, pkgSetting.appId),
+                        "blocking pkg");
                 sendPackageBlockedForUser(packageName, pkgSetting, userId);
             }
         } finally {
@@ -10010,6 +10018,7 @@
                 }
             }
         }
+        sUserManager.systemReady();
     }
 
     public boolean isSafeMode() {
diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java
index d86f2c7..16c2fe7 100644
--- a/services/java/com/android/server/pm/UserManagerService.java
+++ b/services/java/com/android/server/pm/UserManagerService.java
@@ -21,11 +21,13 @@
 import android.app.Activity;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
+import android.app.ActivityThread;
 import android.app.IStopUserCallback;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.RestrictionEntry;
+import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.UserInfo;
@@ -51,6 +53,7 @@
 import android.util.TimeUtils;
 import android.util.Xml;
 
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastXmlSerializer;
 
@@ -229,6 +232,13 @@
                 sInstance = this;
             }
         }
+
+    }
+
+    void systemReady() {
+        mUserPackageMonitor.register(ActivityThread.systemMain().getSystemContext(),
+                null, UserHandle.ALL, false);
+        userForeground(UserHandle.USER_OWNER);
     }
 
     @Override
@@ -873,6 +883,57 @@
         }
     }
 
+    private boolean isPackageInstalled(String pkg, int userId) {
+        final ApplicationInfo info = mPm.getApplicationInfo(pkg,
+                PackageManager.GET_UNINSTALLED_PACKAGES,
+                userId);
+        if (info == null || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Removes all the restrictions files (res_<packagename>) for a given user, if all is true,
+     * else removes only those packages that have been uninstalled.
+     * Does not do any permissions checking.
+     */
+    private void cleanAppRestrictions(int userId, boolean all) {
+        synchronized (mPackagesLock) {
+            File dir = Environment.getUserSystemDirectory(userId);
+            String[] files = dir.list();
+            if (files == null) return;
+            for (String fileName : files) {
+                if (fileName.startsWith(RESTRICTIONS_FILE_PREFIX)) {
+                    File resFile = new File(dir, fileName);
+                    if (resFile.exists()) {
+                        if (all) {
+                            resFile.delete();
+                        } else {
+                            String pkg = fileName.substring(RESTRICTIONS_FILE_PREFIX.length());
+                            if (!isPackageInstalled(pkg, userId)) {
+                                resFile.delete();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes the app restrictions file for a specific package and user id, if it exists.
+     */
+    private void cleanAppRestrictionsForPackage(String pkg, int userId) {
+        synchronized (mPackagesLock) {
+            File dir = Environment.getUserSystemDirectory(userId);
+            File resFile = new File(dir, RESTRICTIONS_FILE_PREFIX + pkg);
+            if (resFile.exists()) {
+                resFile.delete();
+            }
+        }
+    }
+
     @Override
     public UserInfo createUser(String name, int flags) {
         checkManageUsersPermission("Only the system can create users");
@@ -1143,6 +1204,40 @@
         return true;
     }
 
+    @Override
+    public void removeRestrictions() {
+        checkManageUsersPermission("Only system can remove restrictions");
+        final int userHandle = UserHandle.getCallingUserId();
+        synchronized (mPackagesLock) {
+            // Remove all user restrictions
+            setUserRestrictions(new Bundle(), userHandle);
+            // Remove restrictions pin
+            changeRestrictionsPin(null);
+            // Remove any app restrictions
+            cleanAppRestrictions(userHandle, true);
+        }
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                List<ApplicationInfo> apps =
+                        mPm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES,
+                                userHandle).getList();
+                final long ident = Binder.clearCallingIdentity();
+                try {
+                    for (ApplicationInfo appInfo : apps) {
+                        if ((appInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0
+                                && (appInfo.flags & ApplicationInfo.FLAG_BLOCKED) != 0) {
+                            mPm.setApplicationBlockedSettingAsUser(appInfo.packageName, false,
+                                    userHandle);
+                        }
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(ident);
+                }
+            }
+        });
+    }
+
     /*
      * Generate a hash for the given password. To avoid brute force attacks, we use a salted hash.
      * Not the most secure, but it is at least a second level of protection. First level is that
@@ -1347,7 +1442,7 @@
     }
 
     /**
-     * Make a note of the last started time of a user.
+     * Make a note of the last started time of a user and do some cleanup.
      * @param userId the user that was just foregrounded
      */
     public void userForeground(int userId) {
@@ -1362,6 +1457,12 @@
                 user.lastLoggedInTime = now;
                 writeUserLocked(user);
             }
+            // If this is not a restricted profile and there is no restrictions pin, clean up
+            // all restrictions files that might have been left behind, else clean up just the
+            // ones with uninstalled packages
+            RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
+            final long salt = pinState == null ? 0 : pinState.salt;
+            cleanAppRestrictions(userId, (!user.isRestricted() && salt == 0));
         }
     }
 
@@ -1428,4 +1529,17 @@
             }
         }
     }
+
+    private PackageMonitor mUserPackageMonitor = new PackageMonitor() {
+        @Override
+        public void onPackageRemoved(String pkg, int uid) {
+            final int userId = this.getChangingUserId();
+            // Package could be disappearing because it is being blocked, so also check if
+            // it has been uninstalled.
+            final boolean uninstalled = isPackageDisappearing(pkg) == PACKAGE_PERMANENT_CHANGE;
+            if (uninstalled && userId >= 0 && !isPackageInstalled(pkg, userId)) {
+                cleanAppRestrictionsForPackage(pkg, userId);
+            }
+        }
+    };
 }
diff --git a/services/java/com/android/server/power/Notifier.java b/services/java/com/android/server/power/Notifier.java
index e44cfe5..264e2e9 100644
--- a/services/java/com/android/server/power/Notifier.java
+++ b/services/java/com/android/server/power/Notifier.java
@@ -144,7 +144,8 @@
             } else {
                 mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, monitorType);
                 // XXX need to deal with disabled operations.
-                mAppOps.startOperation(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+                mAppOps.startOperation(AppOpsManager.getToken(mAppOps),
+                        AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
             }
         } catch (RemoteException ex) {
             // Ignore
@@ -169,7 +170,8 @@
                 mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, monitorType);
             } else {
                 mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, monitorType);
-                mAppOps.finishOperation(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
+                mAppOps.finishOperation(AppOpsManager.getToken(mAppOps),
+                        AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName);
             }
         } catch (RemoteException ex) {
             // Ignore
diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java
index 5c81cde..777ffe7c 100644
--- a/services/java/com/android/server/power/PowerManagerService.java
+++ b/services/java/com/android/server/power/PowerManagerService.java
@@ -1711,24 +1711,30 @@
             new DisplayPowerController.Callbacks() {
         @Override
         public void onStateChanged() {
-            mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED;
-            updatePowerStateLocked();
+            synchronized (mLock) {
+                mDirty |= DIRTY_ACTUAL_DISPLAY_POWER_STATE_UPDATED;
+                updatePowerStateLocked();
+            }
         }
 
         @Override
         public void onProximityPositive() {
-            mProximityPositive = true;
-            mDirty |= DIRTY_PROXIMITY_POSITIVE;
-            updatePowerStateLocked();
+            synchronized (mLock) {
+                mProximityPositive = true;
+                mDirty |= DIRTY_PROXIMITY_POSITIVE;
+                updatePowerStateLocked();
+            }
         }
 
         @Override
         public void onProximityNegative() {
-            mProximityPositive = false;
-            mDirty |= DIRTY_PROXIMITY_POSITIVE;
-            userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
-                    PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
-            updatePowerStateLocked();
+            synchronized (mLock) {
+                mProximityPositive = false;
+                mDirty |= DIRTY_PROXIMITY_POSITIVE;
+                userActivityNoUpdateLocked(SystemClock.uptimeMillis(),
+                        PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+                updatePowerStateLocked();
+            }
         }
     };
 
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..203bc86
--- /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_VISIBLE_TO_CLIENTS, 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..6445c2e
--- /dev/null
+++ b/services/java/com/android/server/print/RemotePrintSpooler.java
@@ -0,0 +1,612 @@
+/*
+ * 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, 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/updates/CarrierProvisioningUrlsInstallReceiver.java b/services/java/com/android/server/updates/CarrierProvisioningUrlsInstallReceiver.java
new file mode 100644
index 0000000..b53fb65
--- /dev/null
+++ b/services/java/com/android/server/updates/CarrierProvisioningUrlsInstallReceiver.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2012 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.updates;
+
+public class CarrierProvisioningUrlsInstallReceiver extends ConfigUpdateInstallReceiver {
+
+    public CarrierProvisioningUrlsInstallReceiver() {
+        super("/data/misc/radio/", "provisioning_urls.xml", "metadata/", "version");
+    }
+}
diff --git a/services/java/com/android/server/wifi/WifiService.java b/services/java/com/android/server/wifi/WifiService.java
index a70978e..6118503 100644
--- a/services/java/com/android/server/wifi/WifiService.java
+++ b/services/java/com/android/server/wifi/WifiService.java
@@ -48,16 +48,23 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.os.WorkSource;
+import android.os.AsyncTask;
 import android.provider.Settings;
 import android.util.Log;
 import android.util.Slog;
 
+import java.io.FileNotFoundException;
+import java.io.BufferedReader;
 import java.io.FileDescriptor;
+import java.io.FileReader;
+import java.io.IOException;
 import java.io.PrintWriter;
+
 import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.util.ArrayList;
 import java.util.List;
+
 import java.util.concurrent.atomic.AtomicBoolean;
 
 import com.android.internal.R;
@@ -749,6 +756,92 @@
     }
 
     /**
+     * enable TDLS for the local NIC to remote NIC
+     * The APPs don't know the remote MAC address to identify NIC though,
+     * so we need to do additional work to find it from remote IP address
+     */
+
+    class TdlsTaskParams {
+        public String remoteIpAddress;
+        public boolean enable;
+    }
+
+    class TdlsTask extends AsyncTask<TdlsTaskParams, Integer, Integer> {
+        @Override
+        protected Integer doInBackground(TdlsTaskParams... params) {
+
+            // Retrieve parameters for the call
+            TdlsTaskParams param = params[0];
+            String remoteIpAddress = param.remoteIpAddress.trim();
+            boolean enable = param.enable;
+
+            // Get MAC address of Remote IP
+            String macAddress = null;
+
+            BufferedReader reader = null;
+
+            try {
+                reader = new BufferedReader(new FileReader("/proc/net/arp"));
+
+                // Skip over the line bearing colum titles
+                String line = reader.readLine();
+
+                while ((line = reader.readLine()) != null) {
+                    String[] tokens = line.split("[ ]+");
+                    if (tokens.length < 6) {
+                        continue;
+                    }
+
+                    // ARP column format is
+                    // Address HWType HWAddress Flags Mask IFace
+                    String ip = tokens[0];
+                    String mac = tokens[3];
+
+                    if (remoteIpAddress.equals(ip)) {
+                        macAddress = mac;
+                        break;
+                    }
+                }
+
+                if (macAddress == null) {
+                    Slog.w(TAG, "Did not find remoteAddress {" + remoteIpAddress + "} in " +
+                            "/proc/net/arp");
+                } else {
+                    enableTdlsWithMacAddress(macAddress, enable);
+                }
+
+            } catch (FileNotFoundException e) {
+                Slog.e(TAG, "Could not open /proc/net/arp to lookup mac address");
+            } catch (IOException e) {
+                Slog.e(TAG, "Could not read /proc/net/arp to lookup mac address");
+            } finally {
+                try {
+                    if (reader != null) {
+                        reader.close();
+                    }
+                }
+                catch (IOException e) {
+                    // Do nothing
+                }
+            }
+
+            return 0;
+        }
+    }
+
+    public void enableTdls(String remoteAddress, boolean enable) {
+        TdlsTaskParams params = new TdlsTaskParams();
+        params.remoteIpAddress = remoteAddress;
+        params.enable = enable;
+        new TdlsTask().execute(params);
+    }
+
+
+    public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) {
+        mWifiStateMachine.enableTdls(remoteMacAddress, enable);
+    }
+
+    /**
      * Get a reference to handler. This is used by a client to establish
      * an AsyncChannel communication with WifiService
      */
diff --git a/services/java/com/android/server/wm/AppTransition.java b/services/java/com/android/server/wm/AppTransition.java
index 30019e7..cd3daaa 100644
--- a/services/java/com/android/server/wm/AppTransition.java
+++ b/services/java/com/android/server/wm/AppTransition.java
@@ -147,6 +147,8 @@
     private final Interpolator mDecelerateInterpolator;
     private final Interpolator mThumbnailFadeoutInterpolator;
 
+    private int mCurrentUserId = 0;
+
     AppTransition(Context context, Handler h) {
         mContext = context;
         mH = h;
@@ -259,7 +261,7 @@
             if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
                     + packageName);
             return AttributeCache.instance().get(packageName, resId,
-                    com.android.internal.R.styleable.WindowAnimation);
+                    com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
         }
         return null;
     }
@@ -274,7 +276,7 @@
             if (DEBUG_ANIM) Slog.v(TAG, "Loading animations: picked package="
                     + packageName);
             return AttributeCache.instance().get(packageName, resId,
-                    com.android.internal.R.styleable.WindowAnimation);
+                    com.android.internal.R.styleable.WindowAnimation, mCurrentUserId);
         }
         return null;
     }
@@ -758,4 +760,8 @@
             pw.println(mNextAppTransitionCallback);
         }
     }
+
+    public void setCurrentUser(int newUserId) {
+        mCurrentUserId = newUserId;
+    }
 }
diff --git a/services/java/com/android/server/wm/DisplayContent.java b/services/java/com/android/server/wm/DisplayContent.java
index feac370d..4f699ae 100644
--- a/services/java/com/android/server/wm/DisplayContent.java
+++ b/services/java/com/android/server/wm/DisplayContent.java
@@ -25,7 +25,6 @@
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.util.Slog;
-import android.util.SparseArray;
 import android.view.Display;
 import android.view.DisplayInfo;
 
@@ -106,8 +105,6 @@
     /** Detect user tapping outside of current focused stack bounds .*/
     Region mTouchExcludeRegion = new Region();
 
-    SparseArray<UserStacks> mUserStacks = new SparseArray<UserStacks>();
-
     /** Save allocating when retrieving tasks */
     ArrayList<Task> mTmpTasks = new ArrayList<Task>();
 
@@ -156,28 +153,16 @@
         mStackHistory.add(toTop ? mStackHistory.size() : 0, stack);
     }
 
+    public boolean isPrivate() {
+        return (mDisplay.getFlags() & Display.FLAG_PRIVATE) != 0;
+    }
+
     /**
      * Retrieve the tasks on this display in stack order from the bottommost TaskStack up.
      * @return All the Tasks, in order, on this display.
      */
     ArrayList<Task> getTasks() {
         mTmpTasks.clear();
-        // First do the tasks belonging to other users.
-        final int numUserStacks = mUserStacks.size();
-        for (int i = 0; i < numUserStacks; ++i) {
-            UserStacks userStacks = mUserStacks.valueAt(i);
-            ArrayList<TaskStack> stacks = userStacks.mSavedStackHistory;
-            final int numStacks = stacks.size();
-            for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
-                TaskStack stack = stacks.get(stackNdx);
-                if (stack != mHomeStack) {
-                    if (WindowManagerService.DEBUG_LAYERS) Slog.i(TAG, "getTasks: mStackHistory=" +
-                            mStackHistory);
-                    mTmpTasks.addAll(stack.getTasks());
-                }
-            }
-        }
-        // Now do the current user's tasks.
         final int numStacks = mStackHistory.size();
         for (int stackNdx = 0; stackNdx < numStacks; ++stackNdx) {
             mTmpTasks.addAll(mStackHistory.get(stackNdx).getTasks());
@@ -355,14 +340,6 @@
                 return bounds;
             }
         }
-        // Not in the visible stacks, try the saved ones.
-        for (int userNdx = mUserStacks.size() - 1; userNdx >= 0; --userNdx) {
-            UserStacks userStacks = mUserStacks.valueAt(userNdx);
-            Rect bounds = userStacks.mSavedStackBox.getStackBounds(stackId);
-            if (bounds != null) {
-                return bounds;
-            }
-        }
         return null;
     }
 
@@ -396,12 +373,9 @@
                 win.hideLw(false);
             }
         }
-        // Clear the old user's non-home StackBox
-        mUserStacks.put(oldUserId, new UserStacks());
-        UserStacks userStacks = mUserStacks.get(newUserId);
-        if (userStacks != null) {
-            userStacks.restore();
-            mUserStacks.delete(newUserId);
+
+        for (int stackBoxNdx = mStackBoxes.size() - 1; stackBoxNdx >= 0; --stackBoxNdx) {
+            mStackBoxes.get(stackBoxNdx).switchUserStacks(newUserId);
         }
     }
 
@@ -504,49 +478,6 @@
                   token.dump(pw, "    ");
             }
         }
-        if (mUserStacks.size() > 0) {
-            pw.println();
-            pw.println("  Saved user stacks:");
-            for (int i = 0; i < mUserStacks.size(); ++i) {
-                UserStacks userStacks = mUserStacks.valueAt(i);
-                pw.print("  UserId="); pw.println(Integer.toHexString(mUserStacks.keyAt(i)));
-                pw.print("  StackHistory="); pw.println(userStacks.mSavedStackHistory);
-                pw.print("  StackBox="); userStacks.mSavedStackBox.dump("    ", pw);
-            }
-        }
         pw.println();
     }
-
-    private final class UserStacks {
-        final ArrayList<TaskStack> mSavedStackHistory;
-        StackBox mSavedStackBox;
-        int mBoxNdx;
-
-        public UserStacks() {
-            mSavedStackHistory = new ArrayList<TaskStack>(mStackHistory);
-            for (int stackNdx = mStackHistory.size() - 1; stackNdx >=0; --stackNdx) {
-                if (mStackHistory.get(stackNdx) != mHomeStack) {
-                    mStackHistory.remove(stackNdx);
-                }
-            }
-            mSavedStackBox = null;
-            mBoxNdx = -1;
-            for (int boxNdx = mStackBoxes.size() - 1; boxNdx >= 0; --boxNdx) {
-                StackBox box = mStackBoxes.get(boxNdx);
-                if (box.mStack != mHomeStack) {
-                    mSavedStackBox = box;
-                    mBoxNdx = boxNdx;
-                    mStackBoxes.remove(boxNdx);
-                    break;
-                }
-            }
-        }
-
-        void restore() {
-            mStackHistory = mSavedStackHistory;
-            if (mBoxNdx >= 0) {
-                mStackBoxes.add(mBoxNdx, mSavedStackBox);
-            }
-        }
-    }
 }
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/StackBox.java b/services/java/com/android/server/wm/StackBox.java
index d352464..d4cf4e2 100644
--- a/services/java/com/android/server/wm/StackBox.java
+++ b/services/java/com/android/server/wm/StackBox.java
@@ -109,8 +109,8 @@
      * @return true if the specified StackBox matches this or one of its descendants.
      */
     boolean contains(int stackBoxId) {
-        return mStackBoxId == stackBoxId || mFirst.contains(stackBoxId)
-                || mSecond.contains(stackBoxId);
+        return mStackBoxId == stackBoxId ||
+                (mStack == null &&  (mFirst.contains(stackBoxId) || mSecond.contains(stackBoxId)));
     }
 
     /**
@@ -371,6 +371,15 @@
         mSecond.stopDimmingIfNeeded();
     }
 
+    void switchUserStacks(int userId) {
+        if (mStack != null) {
+            mStack.switchUser(userId);
+            return;
+        }
+        mFirst.switchUserStacks(userId);
+        mSecond.switchUserStacks(userId);
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("mParent="); pw.println(mParent);
         pw.print(prefix); pw.print("mBounds="); pw.print(mBounds.toShortString());
diff --git a/services/java/com/android/server/wm/Task.java b/services/java/com/android/server/wm/Task.java
index 88eb96e..d9acbb9 100644
--- a/services/java/com/android/server/wm/Task.java
+++ b/services/java/com/android/server/wm/Task.java
@@ -21,11 +21,13 @@
     TaskStack mStack;
     final AppTokenList mAppTokens = new AppTokenList();
     final int taskId;
+    final int mUserId;
 
-    Task(AppWindowToken wtoken, TaskStack stack) {
+    Task(AppWindowToken wtoken, TaskStack stack, int userId) {
         taskId = wtoken.groupId;
         mAppTokens.add(wtoken);
         mStack = stack;
+        mUserId = userId;
     }
 
     DisplayContent getDisplayContent() {
diff --git a/services/java/com/android/server/wm/TaskStack.java b/services/java/com/android/server/wm/TaskStack.java
index b43a7a1..29156be 100644
--- a/services/java/com/android/server/wm/TaskStack.java
+++ b/services/java/com/android/server/wm/TaskStack.java
@@ -87,15 +87,38 @@
         return mStackId == HOME_STACK_ID;
     }
 
+    boolean hasSibling() {
+        return mStackBox.mParent != null;
+    }
+
     /**
      * Put a Task in this stack. Used for adding and moving.
      * @param task The task to add.
      * @param toTop Whether to add it to the top or bottom.
      */
     boolean addTask(Task task, boolean toTop) {
-        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop);
         mStackBox.makeDirty();
-        mTasks.add(toTop ? mTasks.size() : 0, task);
+
+        int stackNdx;
+        if (!toTop) {
+            stackNdx = 0;
+        } else {
+            stackNdx = mTasks.size();
+            final int currentUserId = mService.mCurrentUserId;
+            if (task.mUserId != currentUserId) {
+                // Place the task below all current user tasks.
+                while (--stackNdx >= 0) {
+                    if (currentUserId != mTasks.get(stackNdx).mUserId) {
+                        break;
+                    }
+                }
+                ++stackNdx;
+            }
+        }
+        if (DEBUG_TASK_MOVEMENT) Slog.d(TAG, "addTask: task=" + task + " toTop=" + toTop
+                + " pos=" + stackNdx);
+        mTasks.add(stackNdx, task);
+
         task.mStack = this;
         return mDisplayContent.moveHomeStackBox(mStackId == HOME_STACK_ID);
     }
@@ -252,6 +275,18 @@
         }
     }
 
+    void switchUser(int userId) {
+        int top = mTasks.size();
+        for (int taskNdx = 0; taskNdx < top; ++taskNdx) {
+            Task task = mTasks.get(taskNdx);
+            if (task.mUserId == userId) {
+                mTasks.remove(taskNdx);
+                mTasks.add(task);
+                --top;
+            }
+        }
+    }
+
     public void dump(String prefix, PrintWriter pw) {
         pw.print(prefix); pw.print("mStackId="); pw.println(mStackId);
         for (int taskNdx = 0; taskNdx < mTasks.size(); ++taskNdx) {
diff --git a/services/java/com/android/server/wm/WindowAnimator.java b/services/java/com/android/server/wm/WindowAnimator.java
index 3fc3ac6..2688cff 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.valueAt(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 ee2ef37..f763068 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -25,6 +25,7 @@
 import android.app.AppOpsManager;
 import android.util.TimeUtils;
 import android.view.IWindowId;
+
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.policy.PolicyManager;
 import com.android.internal.policy.impl.PhoneWindowManager;
@@ -149,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
@@ -283,6 +283,9 @@
     private static final String SYSTEM_SECURE = "ro.secure";
     private static final String SYSTEM_DEBUGGABLE = "ro.debuggable";
 
+    private static final String DENSITY_OVERRIDE = "ro.config.density_override";
+    private static final String SIZE_OVERRIDE = "ro.config.size_override";
+
     private static final int MAX_SCREENSHOT_RETRIES = 3;
 
     final private KeyguardDisableHandler mKeyguardDisableHandler;
@@ -425,9 +428,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;
@@ -2136,6 +2137,11 @@
                 }
             }
 
+            if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {
+                Slog.w(TAG, "Attempted to add private presentation window to a non-private display.  Aborting.");
+                return WindowManagerGlobal.ADD_PERMISSION_DENIED;
+            }
+
             boolean addToken = false;
             WindowToken token = mTokenMap.get(attrs.token);
             if (token == null) {
@@ -2531,13 +2537,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);
+                    }
                 }
             }
         }
@@ -3364,7 +3374,7 @@
 
     @Override
     public void addAppToken(int addPos, IApplicationToken token, int taskId, int stackId,
-            int requestedOrientation, boolean fullscreen, boolean showWhenLocked) {
+            int requestedOrientation, boolean fullscreen, boolean showWhenLocked, int userId) {
         if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,
                 "addAppToken()")) {
             throw new SecurityException("Requires MANAGE_APP_TOKENS permission");
@@ -3397,7 +3407,7 @@
             atoken.showWhenLocked = showWhenLocked;
             atoken.requestedOrientation = requestedOrientation;
             if (DEBUG_TOKEN_MOVEMENT || DEBUG_ADD_REMOVE) Slog.v(TAG, "addAppToken: " + atoken
-                    + " at " + addPos);
+                    + " to stack=" + stackId + " task=" + taskId + " at " + addPos);
 
             Task task = mTaskIdToTask.get(taskId);
             if (task == null) {
@@ -3405,7 +3415,7 @@
                 if (stack == null) {
                     throw new IllegalArgumentException("addAppToken: invalid stackId=" + stackId);
                 }
-                task = new Task(atoken, stack);
+                task = new Task(atoken, stack, userId);
                 stack.addTask(task, true);
                 stack.getDisplayContent().moveStack(stack, true);
                 mTaskIdToTask.put(taskId, task);
@@ -4047,7 +4057,7 @@
                     + Integer.toHexString(theme));
             if (theme != 0) {
                 AttributeCache.Entry ent = AttributeCache.instance().get(pkg, theme,
-                        com.android.internal.R.styleable.Window);
+                        com.android.internal.R.styleable.Window, mCurrentUserId);
                 if (ent == null) {
                     // Whoops!  App doesn't exist.  Um.  Okay.  We'll just
                     // pretend like we didn't see that.
@@ -4117,10 +4127,11 @@
         }
     }
 
-    public void setAppFullscreen(IBinder token) {
+    public void setAppFullscreen(IBinder token, boolean toOpaque) {
         AppWindowToken atoken = findAppWindowToken(token);
         if (atoken != null) {
-            atoken.appFullscreen = true;
+            atoken.appFullscreen = toOpaque;
+            requestTraversal();
         }
     }
 
@@ -4544,9 +4555,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();
@@ -4562,10 +4573,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));
+            }
         }
     }
 
@@ -4816,9 +4829,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) {
@@ -4881,9 +4894,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;
                 }
@@ -4900,9 +4913,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;
             }
@@ -5029,13 +5042,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) {
+                        }
                     }
                 }
             }
@@ -5055,13 +5072,11 @@
             throw new SecurityException("Requires SET_ANIMATION_SCALE permission");
         }
 
-        if (scale < 0) scale = 0;
-        else if (scale > 20) scale = 20;
-        scale = Math.abs(scale);
+        scale = fixScale(scale);
         switch (which) {
-            case 0: mWindowAnimationScale = fixScale(scale); break;
-            case 1: mTransitionAnimationScale = fixScale(scale); break;
-            case 2: mAnimatorDurationScale = fixScale(scale); break;
+            case 0: mWindowAnimationScale = scale; break;
+            case 1: mTransitionAnimationScale = scale; break;
+            case 2: mAnimatorDurationScale = scale; break;
         }
 
         // Persist setting
@@ -5169,12 +5184,13 @@
         synchronized (mWindowMap) {
             int oldUserId = mCurrentUserId;
             mCurrentUserId = newUserId;
+            mAppTransition.setCurrentUser(newUserId);
             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);
             }
@@ -5424,12 +5440,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) {
@@ -6189,9 +6209,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());
             }
         }
 
@@ -6419,11 +6440,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;
+                    }
                 }
             }
         }
@@ -6974,12 +6999,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
+                }
             }
         }
     }
@@ -7020,6 +7049,7 @@
 
         public static final int CLIENT_FREEZE_TIMEOUT = 30;
         public static final int TAP_OUTSIDE_STACK = 31;
+        public static final int NOTIFY_ACTIVITY_DRAWN = 32;
 
         @Override
         public void handleMessage(Message msg) {
@@ -7452,6 +7482,13 @@
                         }
                     }
                 }
+                break;
+                case NOTIFY_ACTIVITY_DRAWN:
+                    try {
+                        mActivityManager.notifyActivityDrawn((IBinder) msg.obj);
+                    } catch (RemoteException e) {
+                    }
+                    break;
             }
             if (DEBUG_WINDOW_TRACE) {
                 Slog.v(TAG, "handleMessage: exit");
@@ -7590,8 +7627,11 @@
     }
 
     private void readForcedDisplaySizeAndDensityLocked(final DisplayContent displayContent) {
-        final String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
+        String sizeStr = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.DISPLAY_SIZE_FORCED);
+        if (sizeStr == null) {
+            sizeStr = SystemProperties.get(SIZE_OVERRIDE, null);
+        }
         if (sizeStr != null && sizeStr.length() > 0) {
             final int pos = sizeStr.indexOf(',');
             if (pos > 0 && sizeStr.lastIndexOf(',') == pos) {
@@ -7611,8 +7651,11 @@
                 }
             }
         }
-        final String densityStr = Settings.Global.getString(mContext.getContentResolver(),
+        String densityStr = Settings.Global.getString(mContext.getContentResolver(),
                 Settings.Global.DISPLAY_DENSITY_FORCED);
+        if (densityStr == null) {
+            densityStr = SystemProperties.get(DENSITY_OVERRIDE, null);
+        }
         if (densityStr != null && densityStr.length() > 0) {
             int density;
             try {
@@ -7833,9 +7876,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);
         }
     }
 
@@ -8759,6 +8803,7 @@
                                 + " interesting=" + numInteresting
                                 + " drawn=" + wtoken.numDrawnWindows);
                         wtoken.allDrawn = true;
+                        mH.obtainMessage(H.NOTIFY_ACTIVITY_DRAWN, wtoken.token).sendToTarget();
                     }
                 }
             }
@@ -8783,9 +8828,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;
             }
@@ -8823,10 +8868,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();
@@ -9238,9 +9282,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);
@@ -9289,9 +9332,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;
             }
@@ -9472,9 +9514,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;
             }
         }
@@ -9553,35 +9596,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;
+                        }
                     }
                 }
             }
@@ -9589,27 +9636,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) {
+                        }
                     }
                 }
             }
@@ -10208,9 +10258,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");
@@ -10226,13 +10277,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) {
@@ -10385,9 +10439,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());
                     }
@@ -10421,11 +10474,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);
+                        }
                     }
                 }
             }
@@ -10438,15 +10495,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);
                     }
                 }
             }
@@ -10698,104 +10759,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/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java
index dd19f89..0129acc 100644
--- a/services/java/com/android/server/wm/WindowState.java
+++ b/services/java/com/android/server/wm/WindowState.java
@@ -461,8 +461,9 @@
     public void computeFrameLw(Rect pf, Rect df, Rect of, Rect cf, Rect vf) {
         mHaveFrame = true;
 
-        if (mAppToken != null) {
-            mContainingFrame.set(getStackBounds());
+        TaskStack stack = mAppToken != null ? getStack() : null;
+        if (stack != null && stack.hasSibling()) {
+            mContainingFrame.set(getStackBounds(stack));
             if (mUnderStatusBar) {
                 mContainingFrame.top = pf.top;
             }
@@ -714,7 +715,10 @@
     }
 
     Rect getStackBounds() {
-        TaskStack stack = getStack();
+        return getStackBounds(getStack());
+    }
+
+    private Rect getStackBounds(TaskStack stack) {
         if (stack != null) {
             return stack.mStackBox.mBounds;
         }
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index f332350..957c448 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -23,6 +23,7 @@
     $(JNI_H_INCLUDE) \
     frameworks/base/services \
     frameworks/base/core/jni \
+    frameworks/native/services \
     external/skia/include/core \
     libcore/include \
     libcore/include/libsuspend \
@@ -32,16 +33,17 @@
 LOCAL_SHARED_LIBRARIES := \
     libandroid_runtime \
     libandroidfw \
+    libbinder \
     libcutils \
     liblog \
     libhardware \
     libhardware_legacy \
     libnativehelper \
-    libsystem_server \
     libutils \
     libui \
     libinput \
     libinputservice \
+    libsensorservice \
     libskia \
     libgui \
     libusbhost \
diff --git a/services/jni/com_android_server_SystemServer.cpp b/services/jni/com_android_server_SystemServer.cpp
index ae29405..0625544 100644
--- a/services/jni/com_android_server_SystemServer.cpp
+++ b/services/jni/com_android_server_SystemServer.cpp
@@ -13,19 +13,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
+#include <jni.h>
+#include <JNIHelp.h>
+
+#include <sensorservice/SensorService.h>
+
+#include <cutils/properties.h>
 #include <utils/Log.h>
 #include <utils/misc.h>
 
-#include "jni.h"
-#include "JNIHelp.h"
-
 namespace android {
 
-extern "C" int system_init();
-
-static void android_server_SystemServer_init1(JNIEnv* env, jobject clazz)
-{
-    system_init();
+static void android_server_SystemServer_nativeInit(JNIEnv* env, jobject clazz) {
+    char propBuf[PROPERTY_VALUE_MAX];
+    property_get("system_init.startsensorservice", propBuf, "1");
+    if (strcmp(propBuf, "1") == 0) {
+        // Start the sensor service
+        SensorService::instantiate();
+    }
 }
 
 /*
@@ -33,7 +39,7 @@
  */
 static JNINativeMethod gMethods[] = {
     /* name, signature, funcPtr */
-    { "init1", "([Ljava/lang/String;)V", (void*) android_server_SystemServer_init1 },
+    { "nativeInit", "()V", (void*) android_server_SystemServer_nativeInit },
 };
 
 int register_android_server_SystemServer(JNIEnv* env)
@@ -43,5 +49,3 @@
 }
 
 }; // namespace android
-
-
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/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index cdc4d78..aca77b8 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -878,8 +878,7 @@
         mAlarmManager.remove(isA(PendingIntent.class));
         expectLastCall().anyTimes();
 
-        mAlarmManager.setInexactRepeating(
-                eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class));
+        mAlarmManager.set(eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), anyLong(), isA(PendingIntent.class));
         expectLastCall().atLeastOnce();
 
         mNetManager.setGlobalAlert(anyLong());
diff --git a/telephony/java/com/android/internal/telephony/TelephonyIntents.java b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
index 3cfd0bf..65bdacf 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyIntents.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyIntents.java
@@ -237,6 +237,23 @@
     public static final String ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS
             = "android.intent.action.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS";
 
+    /**
+     * Activity Action: Start this activity to invoke the carrier setup app.
+     * To filter the intent, see {@link #CATEGORY_MCCMNC_PREFIX}.
+     *
+     * <p class="note">Callers of this should hold the android.permission.INVOKE_CARRIER_SETUP
+     * permission.</p>
+     */
+    public static final String ACTION_CARRIER_SETUP = "android.intent.action.ACTION_CARRIER_SETUP";
+
+    /**
+     * A <em>prefix</em> for the MCC/MNC filtering used with {@link #ACTION_CARRIER_SETUP}.
+     * The MCC/MNC will be concatenated (zero-padded to 3 digits each) to create a final
+     * string of the form:
+     * <br />
+     * <code>android.intent.category.MCCMNC_310260</code>
+     */
+    public static final String CATEGORY_MCCMNC_PREFIX = "android.intent.category.MCCMNC_";
 
     /**
      * Broadcast Action: A "secret code" has been entered in the dialer. Secret codes are
diff --git a/tests/CanvasCompare/res/layout/manual_layout.xml b/tests/CanvasCompare/res/layout/manual_layout.xml
index d7838eb..1a9288c 100644
--- a/tests/CanvasCompare/res/layout/manual_layout.xml
+++ b/tests/CanvasCompare/res/layout/manual_layout.xml
@@ -64,7 +64,7 @@
         </LinearLayout>
     </LinearLayout>
 
-    <com.android.test.hwuicompare.NearestImageView
+    <ImageView
         android:id="@+id/compare_image_view"
         android:layout_width="@dimen/layer_width_double"
         android:layout_height="@dimen/layer_height_double"
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
index 400dff0..405ff65 100644
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
+++ b/tests/CanvasCompare/src/com/android/test/hwuicompare/ManualActivity.java
@@ -82,6 +82,7 @@
                         mCompareImageView.setImageBitmap(mCompareBitmap);
                         break;
                 }
+                mCompareImageView.getDrawable().setFilterBitmap(false);
                 mCompareImageView.invalidate();
             }
 
diff --git a/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java b/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java
deleted file mode 100644
index 542b55a..0000000
--- a/tests/CanvasCompare/src/com/android/test/hwuicompare/NearestImageView.java
+++ /dev/null
@@ -1,47 +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.test.hwuicompare;
-
-import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
-import android.util.AttributeSet;
-import android.widget.ImageView;
-
-public class NearestImageView extends ImageView {
-    public NearestImageView(Context context) {
-        super(context);
-    }
-
-    public NearestImageView(Context context, AttributeSet attrs) {
-        super(context, attrs);
-    }
-
-    public NearestImageView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
-    }
-
-    final PaintFlagsDrawFilter mFilter = new PaintFlagsDrawFilter(Paint.FILTER_BITMAP_FLAG, 0);
-
-    @Override
-    public void onDraw(Canvas canvas) {
-        canvas.setDrawFilter(mFilter);
-        super.onDraw(canvas);
-        canvas.setDrawFilter(null);
-    }
-}
\ No newline at end of file
diff --git a/tests/TransitionTests/AndroidManifest.xml b/tests/TransitionTests/AndroidManifest.xml
index 5483f64..35e7b69 100644
--- a/tests/TransitionTests/AndroidManifest.xml
+++ b/tests/TransitionTests/AndroidManifest.xml
@@ -233,6 +233,20 @@
                 <category android:name="android.intent.category.LAUNCHER" />
             </intent-filter>
         </activity>
+        <activity android:label="CrossfadeImage"
+                  android:name=".CrossfadeImage">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <activity android:label="CrossfadeMultiple"
+                  android:name=".CrossfadeMultiple">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
 
     </application>
 
diff --git a/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_400.jpg b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_400.jpg
new file mode 100644
index 0000000..3923fd1
--- /dev/null
+++ b/tests/TransitionTests/res/drawable-nodpi/self_portrait_square_400.jpg
Binary files differ
diff --git a/tests/TransitionTests/res/layout/crossfade_image.xml b/tests/TransitionTests/res/layout/crossfade_image.xml
new file mode 100644
index 0000000..c46327a
--- /dev/null
+++ b/tests/TransitionTests/res/layout/crossfade_image.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:id="@+id/container"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/self_portrait_square_100"
+            android:onClick="sendMessage"
+            android:id="@+id/contact_picture"/>
+    <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/self_portrait_square_100"
+            android:onClick="sendMessage"
+            android:id="@+id/contact_picture1"/>
+    <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/self_portrait_square_100"
+            android:onClick="sendMessage"
+            android:id="@+id/contact_picture2"/>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/layout/crossfade_multiple.xml b/tests/TransitionTests/res/layout/crossfade_multiple.xml
new file mode 100644
index 0000000..ca32ecb
--- /dev/null
+++ b/tests/TransitionTests/res/layout/crossfade_multiple.xml
@@ -0,0 +1,84 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <RadioGroup android:orientation="horizontal"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+        <RadioButton android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:onClick="changeTransitionType"
+                     android:id="@+id/reveal"
+                     android:text="@string/reveal"/>
+        <RadioButton android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:onClick="changeTransitionType"
+                     android:id="@+id/crossfade"
+                     android:text="@string/crossfade"/>
+        <RadioButton android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:onClick="changeTransitionType"
+                     android:id="@+id/inout"
+                     android:text="@string/inout"/>
+        <RadioButton android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:onClick="changeTransitionType"
+                     android:id="@+id/textfade1"
+                     android:text="@string/textfade1"/>
+        <RadioButton android:layout_width="wrap_content"
+                     android:layout_height="wrap_content"
+                     android:onClick="changeTransitionType"
+                     android:id="@+id/textfade2"
+                     android:text="@string/textfade2"/>
+    </RadioGroup>
+    <LinearLayout android:orientation="horizontal"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content">
+        <Button
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:onClick="sendMessage"
+                android:text="@string/state1"
+                android:id="@+id/button1"/>
+        <Button
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:onClick="sendMessage"
+                android:text="@string/state2"
+                android:id="@+id/button2"/>
+        <Button
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:onClick="sendMessage"
+                android:text="@string/state3"
+                android:id="@+id/button3"/>
+    </LinearLayout>
+
+    <LinearLayout android:orientation="vertical"
+                  android:id="@+id/container"
+                  android:layout_width="match_parent"
+                  android:layout_height="match_parent">
+        <Button
+                android:layout_width="100dip"
+                android:layout_height="100dip"
+                android:textSize="40dip"
+                android:text="@string/a"
+                android:id="@+id/button"/>
+        <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:textSize="40dip"
+                android:text="@string/state1"
+                android:id="@+id/textview"/>
+        <ImageView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:src="@drawable/self_portrait_square_100"
+                android:onClick="sendMessage"
+                android:id="@+id/imageview"/>
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/tests/TransitionTests/res/values/strings.xml b/tests/TransitionTests/res/values/strings.xml
index 9b80a26..9cf7a94 100644
--- a/tests/TransitionTests/res/values/strings.xml
+++ b/tests/TransitionTests/res/values/strings.xml
@@ -49,4 +49,12 @@
     <string name="button3">Button 3</string>
     <string name="button4">Button 4</string>
     <string name="button5">Button 5</string>
+    <string name="a">A</string>
+    <string name="b">B</string>
+    <string name="c">C</string>
+    <string name="reveal">Reveal</string>
+    <string name="crossfade">Crossfade</string>
+    <string name="inout">In/Out</string>
+    <string name="textfade1">T1</string>
+    <string name="textfade2">T2</string>
 </resources>
diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java
new file mode 100644
index 0000000..28e055f
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeImage.java
@@ -0,0 +1,68 @@
+/*
+ * 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.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.Scene;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import android.widget.ImageView;
+
+public class CrossfadeImage extends Activity {
+    ViewGroup mSceneRoot;
+    static int mCurrentScene;
+    Scene mScene1, mScene2;
+    TransitionManager mTransitionManager;
+    boolean mExpanded = false;
+    Transition mTransition;
+    ImageView mImageView;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.crossfade_image);
+
+        ViewGroup container = (ViewGroup) findViewById(R.id.container);
+        mSceneRoot = container;
+
+        mImageView = (ImageView) findViewById(R.id.contact_picture);
+        mImageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
+
+        Crossfade mCrossfade = new Crossfade();
+        mCrossfade.setTargetIds(R.id.contact_picture);
+
+        TransitionGroup group = new TransitionGroup();
+        group.setDuration(1500);
+        group.addTransitions(mCrossfade, new Move());
+        mTransition = group;
+    }
+
+    public void sendMessage(View view) {
+        TransitionManager.beginDelayedTransition(mSceneRoot, mTransition);
+        if (mExpanded) {
+            mImageView.setImageResource(R.drawable.self_portrait_square_100);
+        } else {
+            mImageView.setImageResource(R.drawable.self_portrait_square_200);
+        }
+        mExpanded = !mExpanded;
+    }
+}
diff --git a/tests/TransitionTests/src/com/android/transitiontests/CrossfadeMultiple.java b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeMultiple.java
new file mode 100644
index 0000000..b82dbd8
--- /dev/null
+++ b/tests/TransitionTests/src/com/android/transitiontests/CrossfadeMultiple.java
@@ -0,0 +1,139 @@
+/*
+ * 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.transitiontests;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.transition.Crossfade;
+import android.view.transition.Move;
+import android.view.transition.TextChange;
+import android.view.transition.Transition;
+import android.view.transition.TransitionGroup;
+import android.view.transition.TransitionManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import static android.widget.LinearLayout.LayoutParams;
+
+public class CrossfadeMultiple extends Activity {
+    ViewGroup mSceneRoot;
+    static int mCurrentScene;
+    TransitionManager mTransitionManager;
+    Transition mTransition;
+    ImageView mImageView;
+    TextView mTextView;
+    Button mButton;
+    Crossfade mCrossfade;
+    TransitionGroup mCrossfadeGroup;
+    TransitionGroup mTextChangeGroup1, mTextChangeGroup2;
+    TransitionGroup mInOutGroup;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.crossfade_multiple);
+
+        ViewGroup container = (ViewGroup) findViewById(R.id.container);
+        mSceneRoot = container;
+
+        mButton = (Button) findViewById(R.id.button);
+        mImageView = (ImageView) findViewById(R.id.imageview);
+        mTextView = (TextView) findViewById(R.id.textview);
+
+        mCrossfade = new Crossfade();
+        mCrossfade.setTargetIds(R.id.button, R.id.textview, R.id.imageview);
+
+        mCrossfadeGroup = new TransitionGroup();
+        mCrossfadeGroup.setDuration(300);
+        mCrossfadeGroup.addTransitions(mCrossfade, new Move());
+        mTransition = mCrossfadeGroup;
+
+        mInOutGroup = new TransitionGroup();
+        Crossfade inOut = new Crossfade();
+        inOut.setDuration(300);
+        inOut.setFadeBehavior(Crossfade.FADE_BEHAVIOR_OUT_IN);
+        Move move = new Move();
+        move.setStartDelay(150);
+        move.setDuration(0);
+        mInOutGroup.addTransitions(inOut, move);
+
+        mTextChangeGroup1 = new TransitionGroup();
+        TextChange textChangeInOut = new TextChange();
+        textChangeInOut.setChangeBehavior(TextChange.CHANGE_BEHAVIOR_OUT_IN);
+        mTextChangeGroup1.addTransitions(textChangeInOut, new Move());
+
+        mTextChangeGroup2 = new TransitionGroup();
+        mTextChangeGroup2.setOrdering(TransitionGroup.SEQUENTIALLY);
+        TextChange textChangeOut = new TextChange();
+        textChangeOut.setChangeBehavior(TextChange.CHANGE_BEHAVIOR_OUT);
+        TextChange textChangeIn = new TextChange();
+        textChangeIn.setChangeBehavior(TextChange.CHANGE_BEHAVIOR_IN);
+        mTextChangeGroup2.addTransitions(textChangeOut, new Move(), textChangeIn);
+    }
+
+    public void sendMessage(View view) {
+        TransitionManager.beginDelayedTransition(mSceneRoot, mTransition);
+        int id = view.getId();
+        LayoutParams params = null;
+        switch (id) {
+            case R.id.button1:
+                params = new LayoutParams(200, 200);
+                mButton.setText("A");
+                mTextView.setText("1111111");
+                mImageView.setImageResource(R.drawable.self_portrait_square_100);
+                break;
+            case R.id.button2:
+                params = new LayoutParams(400, 200);
+                mButton.setText("B");
+                mTextView.setText("2222222");
+                mImageView.setImageResource(R.drawable.self_portrait_square_200);
+                break;
+            case R.id.button3:
+                params = new LayoutParams(200, 400);
+                mButton.setText("C");
+                mTextView.setText("3333333");
+                mImageView.setImageResource(R.drawable.self_portrait_square_400);
+                break;
+        }
+        mButton.setLayoutParams(params);
+    }
+
+    public void changeTransitionType(View view) {
+        int id = view.getId();
+        switch (id) {
+            case R.id.reveal:
+                mCrossfade.setFadeBehavior(Crossfade.FADE_BEHAVIOR_REVEAL);
+                mTransition = mCrossfadeGroup;
+                break;
+            case R.id.crossfade:
+                mCrossfade.setFadeBehavior(Crossfade.FADE_BEHAVIOR_CROSSFADE);
+                mTransition = mCrossfadeGroup;
+                break;
+            case R.id.inout:
+                mTransition = mInOutGroup;
+                break;
+            case R.id.textfade1:
+                mTransition = mTextChangeGroup1;
+                break;
+            case R.id.textfade2:
+                mTransition = mTextChangeGroup2;
+                break;
+        }
+    }
+}
diff --git a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
index 53318a4..e4c4214 100644
--- a/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
+++ b/tests/permission/src/com/android/framework/permission/tests/WindowManagerPermissionTests.java
@@ -93,7 +93,7 @@
         }
         
         try {
-            mWm.addAppToken(0, null, 0, 0, 0, false, false);
+            mWm.addAppToken(0, null, 0, 0, 0, false, false, 0);
             fail("IWindowManager.addAppToken did not throw SecurityException as"
                     + " expected");
         } catch (SecurityException e) {
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index b27ce0e..4e73568 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -25,8 +25,8 @@
 # We need to process the framework classes.jar file, but we can't
 # depend directly on it (private vars won't be inherited correctly).
 # So, we depend on framework's BUILT file.
-built_framework_dep := $(call java-lib-deps,framework)
-built_framework_classes := $(call java-lib-files,framework)
+built_framework_dep := $(call java-lib-deps,framework-base)
+built_framework_classes := $(call java-lib-files,framework-base)
 
 built_core_dep := $(call java-lib-deps,core)
 built_core_classes := $(call java-lib-files,core)
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 6343049..f0c3a75 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -81,7 +81,7 @@
 
     @Override
     public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
-            boolean arg5, boolean arg6)
+            boolean arg5, boolean arg6, int arg7)
             throws RemoteException {
         // TODO Auto-generated method stub
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index cbefd3d..b909bec 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -30,6 +30,7 @@
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.resources.Density;
 import com.android.resources.ResourceType;
+import com.android.resources.ScreenOrientation;
 import com.android.resources.ScreenSize;
 
 import android.content.res.Configuration;
@@ -347,6 +348,23 @@
         config.compatScreenWidthDp = config.screenWidthDp;
         config.compatScreenHeightDp = config.screenHeightDp;
 
+        ScreenOrientation orientation = hardwareConfig.getOrientation();
+        if (orientation != null) {
+            switch (orientation) {
+            case PORTRAIT:
+                config.orientation = Configuration.ORIENTATION_PORTRAIT;
+                break;
+            case LANDSCAPE:
+                config.orientation = Configuration.ORIENTATION_LANDSCAPE;
+                break;
+            case SQUARE:
+                config.orientation = Configuration.ORIENTATION_SQUARE;
+                break;
+            }
+        } else {
+            config.orientation = Configuration.ORIENTATION_UNDEFINED;
+        }
+
         // TODO: fill in more config info.
 
         return config;
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 547ae95..6a4bd45 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -110,5 +110,9 @@
     String getConfigFile();
 
     void captivePortalCheckComplete();
+
+    void enableTdls(String remoteIPAddress, boolean enable);
+
+    void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable);
 }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index a3c172a..86ca4a7 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -32,6 +32,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 
+import java.net.InetAddress;
 import java.util.concurrent.CountDownLatch;
 
 import com.android.internal.util.AsyncChannel;
@@ -1132,6 +1133,43 @@
         }
     }
 
+
+    /**
+     * Enable TDLS on a specific local route
+     *
+     * This API is used by WiFi display, but eventually it can be made public,
+     * just depends on how popular TDLS gets
+     *
+     * @param remoteIPAddress IP address of the endpoint to setup TDLS with
+     * @param enable true = setup and false = tear down TDLS
+     * @hide
+     */
+    public void enableTdls(InetAddress remoteIPAddress, boolean enable) {
+        try {
+            mService.enableTdls(remoteIPAddress.getHostAddress(), enable);
+        } catch (RemoteException e) {
+            // Just ignore the exception
+        }
+    }
+
+    /**
+     * Enable TDLS on a specific local route
+     *
+     * This API is used by WiFi display, but eventually it can be made public,
+     * just depends on how popular TDLS gets
+     *
+     * @param remoteMacAddress MAC address of the remote endpoint such as 00:00:0c:9f:f2:ab
+     * @param enable true = setup and false = tear down TDLS
+     * @hide
+     */
+    public void enableTdlsWithMacAddress(String remoteMacAddress, boolean enable) {
+        try {
+            mService.enableTdlsWithMacAddress(remoteMacAddress, enable);
+        } catch (RemoteException e) {
+            // Just ignore the exception
+        }
+    }
+
     /* TODO: deprecate synchronous API and open up the following API */
 
     private static final int BASE = Protocol.BASE_WIFI_MANAGER;
diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java
index 375a1607..b1dd2ce 100644
--- a/wifi/java/android/net/wifi/WifiNative.java
+++ b/wifi/java/android/net/wifi/WifiNative.java
@@ -370,6 +370,15 @@
         doBooleanCommand("SCAN_INTERVAL " + scanInterval);
     }
 
+    public void startTdls(String macAddr, boolean enable) {
+        if (enable) {
+            doBooleanCommand("TDLS_DISCOVER " + macAddr);
+            doBooleanCommand("TDLS_SETUP " + macAddr);
+        } else {
+            doBooleanCommand("TDLS_TEARDOWN " + macAddr);
+        }
+    }
+
     /** Example output:
      * RSSI=-65
      * LINKSPEED=48
@@ -388,6 +397,10 @@
         return doStringCommand("PKTCNT_POLL");
     }
 
+    public void bssFlush() {
+        doBooleanCommand("BSS_FLUSH");
+    }
+
     public boolean startWpsPbc(String bssid) {
         if (TextUtils.isEmpty(bssid)) {
             return doBooleanCommand("WPS_PBC");
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 1f51b2a..858fbcc 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -337,6 +337,8 @@
     static final int CMD_SET_FREQUENCY_BAND               = BASE + 90;
     /* Enable background scan for configured networks */
     static final int CMD_ENABLE_BACKGROUND_SCAN           = BASE + 91;
+    /* Enable TDLS on a specific MAC address */
+    static final int CMD_ENABLE_TDLS                      = BASE + 92;
 
     /* Commands from/to the SupplicantStateTracker */
     /* Reset the supplicant state tracker */
@@ -1074,6 +1076,14 @@
     }
 
     /**
+     * Enable TDLS for a specific MAC address
+     */
+    public void enableTdls(String remoteMacAddress, boolean enable) {
+        int enabler = enable ? 1 : 0;
+        sendMessage(CMD_ENABLE_TDLS, enabler, 0, remoteMacAddress);
+    }
+
+    /**
      * Returns the operational frequency band
      */
     public int getFrequencyBand() {
@@ -1773,6 +1783,14 @@
         // TODO: Remove this comment when the driver is fixed.
         setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, false);
         mWifiNative.setPowerSave(false);
+
+        /* P2p discovery breaks dhcp, shut it down in order to get through this */
+        Message msg = new Message();
+        msg.what = WifiP2pService.BLOCK_DISCOVERY;
+        msg.arg1 = WifiP2pService.ENABLED;
+        msg.arg2 = DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE;
+        msg.obj = mDhcpStateMachine;
+        mWifiP2pChannel.sendMessage(msg);
     }
 
 
@@ -1799,6 +1817,8 @@
         setSuspendOptimizationsNative(SUSPEND_DUE_TO_DHCP, true);
         mWifiNative.setPowerSave(true);
 
+        mWifiP2pChannel.sendMessage(WifiP2pService.BLOCK_DISCOVERY, WifiP2pService.DISABLED);
+
         // Set the coexistence mode back to its default value
         mWifiNative.setBluetoothCoexistenceMode(
                 mWifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE);
@@ -2497,6 +2517,8 @@
                     if (DBG) log("set frequency band " + band);
                     if (mWifiNative.setBand(band)) {
                         mFrequencyBand.set(band);
+                        // flush old data - like scan results
+                        mWifiNative.bssFlush();
                         //Fetch the latest scan results when frequency band is set
                         startScanNative(WifiNative.SCAN_WITH_CONNECTION_SETUP);
                     } else {
@@ -2593,6 +2615,13 @@
                         setSuspendOptimizationsNative(SUSPEND_DUE_TO_HIGH_PERF, true);
                     }
                     break;
+                case CMD_ENABLE_TDLS:
+                    if (message.obj != null) {
+                        String remoteAddress = (String) message.obj;
+                        boolean enable = (message.arg1 == 1);
+                        mWifiNative.startTdls(remoteAddress, enable);
+                    }
+                    break;
                 default:
                     return NOT_HANDLED;
             }
@@ -2993,7 +3022,6 @@
             switch (message.what) {
               case DhcpStateMachine.CMD_PRE_DHCP_ACTION:
                   handlePreDhcpSetup();
-                  mDhcpStateMachine.sendMessage(DhcpStateMachine.CMD_PRE_DHCP_ACTION_COMPLETE);
                   break;
               case DhcpStateMachine.CMD_POST_DHCP_ACTION:
                   handlePostDhcpSetup();
diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
index 4cfc4ac..68a082a 100644
--- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java
+++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java
@@ -166,6 +166,16 @@
 
     public static final int SET_MIRACAST_MODE               =   BASE + 14;
 
+    // During dhcp (and perhaps other times) we can't afford to drop packets
+    // but Discovery will switch our channel enough we will.
+    //   msg.arg1 = ENABLED for blocking, DISABLED for resumed.
+    //   msg.arg2 = msg to send when blocked
+    //   msg.obj  = StateMachine to send to when blocked
+    public static final int BLOCK_DISCOVERY                 =   BASE + 15;
+
+    public static final int ENABLED                         = 1;
+    public static final int DISABLED                        = 0;
+
     private final boolean mP2pSupported;
 
     private WifiP2pDevice mThisDevice = new WifiP2pDevice();
@@ -182,6 +192,15 @@
      * broadcasts
      */
     private boolean mDiscoveryStarted;
+    /* Track whether servcice/peer discovery is blocked in favor of other wifi actions
+     * (notably dhcp)
+     */
+    private boolean mDiscoveryBlocked;
+
+    /*
+     * remember if we were in a scan when it had to be stopped
+     */
+    private boolean mDiscoveryPostponed = false;
 
     private NetworkInfo mNetworkInfo;
 
@@ -479,6 +498,20 @@
                     AsyncChannel ac = new AsyncChannel();
                     ac.connect(mContext, getHandler(), message.replyTo);
                     break;
+                case BLOCK_DISCOVERY:
+                    mDiscoveryBlocked = (message.arg1 == ENABLED ? true : false);
+                    // always reset this - we went to a state that doesn't support discovery so
+                    // it would have stopped regardless
+                    mDiscoveryPostponed = false;
+                    if (mDiscoveryBlocked) {
+                        try {
+                            StateMachine m = (StateMachine)message.obj;
+                            m.sendMessage(message.arg2);
+                        } catch (Exception e) {
+                            loge("unable to send BLOCK_DISCOVERY response: " + e);
+                        }
+                    }
+                    break;
                 case WifiP2pManager.DISCOVER_PEERS:
                     replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                             WifiP2pManager.BUSY);
@@ -851,7 +884,33 @@
                     }
                     break;
                 }
+                case BLOCK_DISCOVERY:
+                    boolean blocked = (message.arg1 == ENABLED ? true : false);
+                    if (mDiscoveryBlocked == blocked) break;
+                    mDiscoveryBlocked = blocked;
+                    if (blocked && mDiscoveryStarted) {
+                        mWifiNative.p2pStopFind();
+                        mDiscoveryPostponed = true;
+                    }
+                    if (!blocked && mDiscoveryPostponed) {
+                        mDiscoveryPostponed = false;
+                        mWifiNative.p2pFind(DISCOVER_TIMEOUT_S);
+                    }
+                    if (blocked) {
+                        try {
+                            StateMachine m = (StateMachine)message.obj;
+                            m.sendMessage(message.arg2);
+                        } catch (Exception e) {
+                            loge("unable to send BLOCK_DISCOVERY response: " + e);
+                        }
+                    }
+                    break;
                 case WifiP2pManager.DISCOVER_PEERS:
+                    if (mDiscoveryBlocked) {
+                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
+                                WifiP2pManager.BUSY);
+                        break;
+                    }
                     // do not send service discovery request while normal find operation.
                     clearSupplicantServiceRequest();
                     if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {
@@ -874,6 +933,11 @@
                     }
                     break;
                 case WifiP2pManager.DISCOVER_SERVICES:
+                    if (mDiscoveryBlocked) {
+                        replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,
+                                WifiP2pManager.BUSY);
+                        break;
+                    }
                     if (DBG) logd(getName() + " discover services");
                     if (!updateSupplicantServiceRequest()) {
                         replyToMessage(message, WifiP2pManager.DISCOVER_SERVICES_FAILED,