Merge "Clone animators before running in StateListAnimator"
diff --git a/Android.mk b/Android.mk
index 917d579..b784f3d6 100644
--- a/Android.mk
+++ b/Android.mk
@@ -94,8 +94,6 @@
 	core/java/android/app/backup/IFullBackupRestoreObserver.aidl \
 	core/java/android/app/backup/IRestoreObserver.aidl \
 	core/java/android/app/backup/IRestoreSession.aidl \
-	core/java/android/app/maintenance/IIdleCallback.aidl \
-	core/java/android/app/maintenance/IIdleService.aidl \
 	core/java/android/bluetooth/IBluetooth.aidl \
 	core/java/android/bluetooth/IBluetoothA2dp.aidl \
 	core/java/android/bluetooth/IBluetoothA2dpSink.aidl \
@@ -154,6 +152,7 @@
 	core/java/android/hardware/hdmi/IHdmiControlService.aidl \
 	core/java/android/hardware/hdmi/IHdmiDeviceEventListener.aidl \
 	core/java/android/hardware/hdmi/IHdmiHotplugEventListener.aidl \
+	core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl \
 	core/java/android/hardware/input/IInputManager.aidl \
 	core/java/android/hardware/input/IInputDevicesChangedListener.aidl \
 	core/java/android/hardware/location/IFusedLocationHardware.aidl \
@@ -177,6 +176,7 @@
 	core/java/android/nfc/INfcAdapterExtras.aidl \
 	core/java/android/nfc/INfcTag.aidl \
 	core/java/android/nfc/INfcCardEmulation.aidl \
+	core/java/android/nfc/INfcLockscreenDispatch.aidl \
 	core/java/android/os/IBatteryPropertiesListener.aidl \
 	core/java/android/os/IBatteryPropertiesRegistrar.aidl \
 	core/java/android/os/ICancellationSignal.aidl \
@@ -330,8 +330,6 @@
 	telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallServiceLookupResponse.aidl \
 	telecomm/java/com/android/internal/telecomm/ICallServiceProvider.aidl \
-	telecomm/java/com/android/internal/telecomm/ICallServiceSelector.aidl \
-	telecomm/java/com/android/internal/telecomm/ICallServiceSelectorAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallAdapter.aidl \
 	telecomm/java/com/android/internal/telecomm/IInCallService.aidl \
 	telecomm/java/com/android/internal/telecomm/ITelecommService.aidl \
@@ -346,6 +344,7 @@
 	telephony/java/com/android/internal/telephony/ISms.aidl \
 	telephony/java/com/android/internal/telephony/IWapPushManager.aidl \
 	telephony/java/com/android/internal/telephony/ISub.aidl \
+	telephony/java/com/android/internal/telephony/IMms.aidl \
 	wifi/java/android/net/wifi/IWifiManager.aidl \
 	wifi/java/android/net/wifi/passpoint/IWifiPasspointManager.aidl \
 	wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
@@ -363,7 +362,7 @@
 			$(framework_res_source_path)/com/android/internal/R.java
 
 LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := bouncycastle conscrypt core core-junit ext okhttp
+LOCAL_JAVA_LIBRARIES := core-libart conscrypt okhttp core-junit bouncycastle ext
 
 LOCAL_MODULE := framework-base
 
@@ -591,9 +590,9 @@
 	$(framework_res_source_path)/com/android/internal/R.java
 
 framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \
-	bouncycastle \
+	core-libart \
 	conscrypt \
-	core \
+	bouncycastle \
 	okhttp \
 	ext \
 	framework \
@@ -635,7 +634,7 @@
     -since $(SRC_API_DIR)/17.txt 17 \
     -since $(SRC_API_DIR)/18.txt 18 \
     -since $(SRC_API_DIR)/19.txt 19 \
-		-werror -hide 113 \
+		-werror -hide 111 -hide 113 \
 		-overview $(LOCAL_PATH)/core/java/overview.html
 
 framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
@@ -823,6 +822,7 @@
 		$(framework_docs_LOCAL_DROIDDOC_OPTIONS) \
 		-toroot / \
 		-hdf android.whichdoc online \
+		-briefdocs \
 		$(sample_groups) \
 		-hdf android.hasSamples true \
 		-samplesdir $(samples_dir)
@@ -904,7 +904,7 @@
 LOCAL_SRC_FILES := $(ext_src_files)
 
 LOCAL_NO_STANDARD_LIBRARIES := true
-LOCAL_JAVA_LIBRARIES := core
+LOCAL_JAVA_LIBRARIES := core-libart
 LOCAL_JAVA_RESOURCE_DIRS := $(ext_res_dirs)
 LOCAL_MODULE_TAGS := optional
 LOCAL_MODULE := ext
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 5d92792..b6a47f2 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -34,9 +34,9 @@
 # made today requires touching the same file, just copy the old
 # touch step and add it to the end of the list.
 #
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
+# *****************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THE BANNER
+# *****************************************************************
 
 # For example:
 #$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
@@ -194,10 +194,16 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/wearable)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/tv/ITv*)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates)
-
-# ************************************************
-# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
-# ************************************************
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/task)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/task)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/TaskManager)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/src/core/java/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework-base_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/classes/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/android_stubs_current_intermediates/src/android/app/maintenance)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/services_intermediates)
+
+# ******************************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER
+# ******************************************************************
diff --git a/api/current.txt b/api/current.txt
index 8a61cc0..a678773 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -91,6 +91,7 @@
     field public static final java.lang.String NFC = "android.permission.NFC";
     field public static final deprecated java.lang.String PERSISTENT_ACTIVITY = "android.permission.PERSISTENT_ACTIVITY";
     field public static final java.lang.String PROCESS_OUTGOING_CALLS = "android.permission.PROCESS_OUTGOING_CALLS";
+    field public static final java.lang.String READ_ALL_VOICEMAIL = "com.android.voicemail.permission.READ_ALL_VOICEMAIL";
     field public static final java.lang.String READ_CALENDAR = "android.permission.READ_CALENDAR";
     field public static final java.lang.String READ_CALL_LOG = "android.permission.READ_CALL_LOG";
     field public static final java.lang.String READ_CONTACTS = "android.permission.READ_CONTACTS";
@@ -260,10 +261,13 @@
     field public static final int actionModeCloseDrawable = 16843484; // 0x10102dc
     field public static final int actionModeCopyDrawable = 16843538; // 0x1010312
     field public static final int actionModeCutDrawable = 16843537; // 0x1010311
+    field public static final int actionModeFindDrawable = 16843900; // 0x101047c
     field public static final int actionModePasteDrawable = 16843539; // 0x1010313
     field public static final int actionModeSelectAllDrawable = 16843646; // 0x101037e
+    field public static final int actionModeShareDrawable = 16843899; // 0x101047b
     field public static final int actionModeSplitBackground = 16843677; // 0x101039d
     field public static final int actionModeStyle = 16843668; // 0x1010394
+    field public static final int actionModeWebSearchDrawable = 16843901; // 0x101047d
     field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6
     field public static final int actionOverflowMenuStyle = 16843846; // 0x1010446
     field public static final int actionProviderClass = 16843657; // 0x1010389
@@ -320,6 +324,8 @@
     field public static final int backgroundDimEnabled = 16843295; // 0x101021f
     field public static final int backgroundSplit = 16843659; // 0x101038b
     field public static final int backgroundStacked = 16843658; // 0x101038a
+    field public static final int backgroundTint = 16843885; // 0x101046d
+    field public static final int backgroundTintMode = 16843886; // 0x101046e
     field public static final int backupAgent = 16843391; // 0x101027f
     field public static final int banner = 16843762; // 0x10103f2
     field public static final int baseline = 16843548; // 0x101031c
@@ -344,6 +350,8 @@
     field public static final int buttonStyleInset = 16842826; // 0x101004a
     field public static final int buttonStyleSmall = 16842825; // 0x1010049
     field public static final int buttonStyleToggle = 16842827; // 0x101004b
+    field public static final int buttonTint = 16843889; // 0x1010471
+    field public static final int buttonTintMode = 16843890; // 0x1010472
     field public static final int cacheColorHint = 16843009; // 0x1010101
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
@@ -505,7 +513,7 @@
     field public static final int excludeClass = 16843844; // 0x1010444
     field public static final int excludeFromRecents = 16842775; // 0x1010017
     field public static final int excludeId = 16843843; // 0x1010443
-    field public static final int excludeViewName = 16843856; // 0x1010450
+    field public static final int excludeName = 16843856; // 0x1010450
     field public static final int exitFadeDuration = 16843533; // 0x101030d
     field public static final int expandableListPreferredChildIndicatorLeft = 16842834; // 0x1010052
     field public static final int expandableListPreferredChildIndicatorRight = 16842835; // 0x1010053
@@ -554,6 +562,8 @@
     field public static final int footerDividersEnabled = 16843311; // 0x101022f
     field public static final int foreground = 16843017; // 0x1010109
     field public static final int foregroundGravity = 16843264; // 0x1010200
+    field public static final int foregroundTint = 16843887; // 0x101046f
+    field public static final int foregroundTintMode = 16843888; // 0x1010470
     field public static final int format = 16843013; // 0x1010105
     field public static final int format12Hour = 16843722; // 0x10103ca
     field public static final int format24Hour = 16843723; // 0x10103cb
@@ -573,6 +583,7 @@
     field public static final int fromXScale = 16843202; // 0x10101c2
     field public static final int fromYDelta = 16843208; // 0x10101c8
     field public static final int fromYScale = 16843204; // 0x10101c4
+    field public static final int fullBackupOnly = 16843893; // 0x1010475
     field public static final int fullBright = 16842954; // 0x10100ca
     field public static final int fullDark = 16842950; // 0x10100c6
     field public static final int functionalTest = 16842787; // 0x1010023
@@ -636,6 +647,8 @@
     field public static final int indeterminateDuration = 16843069; // 0x101013d
     field public static final int indeterminateOnly = 16843066; // 0x101013a
     field public static final int indeterminateProgressStyle = 16843544; // 0x1010318
+    field public static final int indeterminateTint = 16843883; // 0x101046b
+    field public static final int indeterminateTintMode = 16843884; // 0x101046c
     field public static final int indicatorEnd = 16843730; // 0x10103d2
     field public static final int indicatorLeft = 16843021; // 0x101010d
     field public static final int indicatorRight = 16843022; // 0x101010e
@@ -885,7 +898,7 @@
     field public static final int permissionFlags = 16843719; // 0x10103c7
     field public static final int permissionGroup = 16842762; // 0x101000a
     field public static final int permissionGroupFlags = 16843717; // 0x10103c5
-    field public static final int persistable = 16843823; // 0x101042f
+    field public static final int persistableMode = 16843823; // 0x101042f
     field public static final int persistent = 16842765; // 0x101000d
     field public static final int persistentDrawingCache = 16842990; // 0x10100ee
     field public static final deprecated int phoneNumber = 16843111; // 0x1010167
@@ -911,6 +924,8 @@
     field public static final int privateImeOptions = 16843299; // 0x1010223
     field public static final int process = 16842769; // 0x1010011
     field public static final int progress = 16843063; // 0x1010137
+    field public static final int progressBackgroundTint = 16843879; // 0x1010467
+    field public static final int progressBackgroundTintMode = 16843880; // 0x1010468
     field public static final int progressBarPadding = 16843545; // 0x1010319
     field public static final int progressBarStyle = 16842871; // 0x1010077
     field public static final int progressBarStyleHorizontal = 16842872; // 0x1010078
@@ -921,8 +936,12 @@
     field public static final int progressBarStyleSmallInverse = 16843400; // 0x1010288
     field public static final int progressBarStyleSmallTitle = 16843279; // 0x101020f
     field public static final int progressDrawable = 16843068; // 0x101013c
+    field public static final int progressTint = 16843877; // 0x1010465
+    field public static final int progressTintMode = 16843878; // 0x1010466
     field public static final int prompt = 16843131; // 0x101017b
     field public static final int propertyName = 16843489; // 0x10102e1
+    field public static final int propertyXName = 16843894; // 0x1010476
+    field public static final int propertyYName = 16843895; // 0x1010477
     field public static final int protectionLevel = 16842761; // 0x1010009
     field public static final int publicKey = 16843686; // 0x10103a6
     field public static final int queryActionMsg = 16843227; // 0x10101db
@@ -941,6 +960,7 @@
     field public static final int ratingBarStyleIndicator = 16843280; // 0x1010210
     field public static final int ratingBarStyleSmall = 16842877; // 0x101007d
     field public static final int readPermission = 16842759; // 0x1010007
+    field public static final int relinquishTaskIdentity = 16843896; // 0x1010478
     field public static final int repeatCount = 16843199; // 0x10101bf
     field public static final int repeatMode = 16843200; // 0x10101c0
     field public static final int reqFiveWayNav = 16843314; // 0x1010232
@@ -1013,6 +1033,8 @@
     field public static final int searchSuggestSelection = 16843224; // 0x10101d8
     field public static final int searchSuggestThreshold = 16843373; // 0x101026d
     field public static final int secondaryProgress = 16843064; // 0x1010138
+    field public static final int secondaryProgressTint = 16843881; // 0x1010469
+    field public static final int secondaryProgressTintMode = 16843882; // 0x101046a
     field public static final int seekBarStyle = 16842875; // 0x101007b
     field public static final int segmentedButtonStyle = 16843568; // 0x1010330
     field public static final int selectAllOnFocus = 16843102; // 0x101015e
@@ -1135,9 +1157,9 @@
     field public static final int targetClass = 16842799; // 0x101002f
     field public static final int targetDescriptions = 16843680; // 0x10103a0
     field public static final int targetId = 16843740; // 0x10103dc
+    field public static final int targetName = 16843855; // 0x101044f
     field public static final int targetPackage = 16842785; // 0x1010021
     field public static final int targetSdkVersion = 16843376; // 0x1010270
-    field public static final int targetViewName = 16843855; // 0x101044f
     field public static final int taskAffinity = 16842770; // 0x1010012
     field public static final int taskCloseEnterAnimation = 16842942; // 0x10100be
     field public static final int taskCloseExitAnimation = 16842943; // 0x10100bf
@@ -1216,8 +1238,12 @@
     field public static final int thumb = 16843074; // 0x1010142
     field public static final int thumbOffset = 16843075; // 0x1010143
     field public static final int thumbTextPadding = 16843634; // 0x1010372
+    field public static final int thumbTint = 16843891; // 0x1010473
+    field public static final int thumbTintMode = 16843892; // 0x1010474
     field public static final int thumbnail = 16843429; // 0x10102a5
     field public static final int tileMode = 16843265; // 0x1010201
+    field public static final int tileModeX = 16843897; // 0x1010479
+    field public static final int tileModeY = 16843898; // 0x101047a
     field public static final int timeZone = 16843724; // 0x10103cc
     field public static final int tint = 16843041; // 0x1010121
     field public static final int tintMode = 16843797; // 0x1010415
@@ -1245,6 +1271,7 @@
     field public static final int transformPivotY = 16843553; // 0x1010321
     field public static final int transition = 16843743; // 0x10103df
     field public static final int transitionGroup = 16843803; // 0x101041b
+    field public static final int transitionName = 16843802; // 0x101041a
     field public static final int transitionOrdering = 16843744; // 0x10103e0
     field public static final int translateX = 16843868; // 0x101045c
     field public static final int translateY = 16843869; // 0x101045d
@@ -1279,7 +1306,6 @@
     field public static final int verticalGap = 16843328; // 0x1010240
     field public static final int verticalScrollbarPosition = 16843572; // 0x1010334
     field public static final int verticalSpacing = 16843029; // 0x1010115
-    field public static final int viewName = 16843802; // 0x101041a
     field public static final int viewportHeight = 16843805; // 0x101041d
     field public static final int viewportWidth = 16843804; // 0x101041c
     field public static final int visibility = 16842972; // 0x10100dc
@@ -2504,7 +2530,14 @@
 
   public static final class R.transition {
     ctor public R.transition();
+    field public static final int explode = 17760259; // 0x10f0003
+    field public static final int fade = 17760258; // 0x10f0002
+    field public static final int move = 17760257; // 0x10f0001
     field public static final int no_transition = 17760256; // 0x10f0000
+    field public static final int slide_bottom = 17760260; // 0x10f0004
+    field public static final int slide_left = 17760263; // 0x10f0007
+    field public static final int slide_right = 17760262; // 0x10f0006
+    field public static final int slide_top = 17760261; // 0x10f0005
   }
 
   public static final class R.xml {
@@ -5177,6 +5210,7 @@
     method public java.lang.CharSequence onDisableRequested(android.content.Context, android.content.Intent);
     method public void onDisabled(android.content.Context, android.content.Intent);
     method public void onEnabled(android.content.Context, android.content.Intent);
+    method public void onLockTaskModeChanged(android.content.Context, android.content.Intent, boolean, java.lang.String);
     method public void onPasswordChanged(android.content.Context, android.content.Intent);
     method public void onPasswordExpiring(android.content.Context, android.content.Intent);
     method public void onPasswordFailed(android.content.Context, android.content.Intent);
@@ -5186,6 +5220,7 @@
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLED = "android.app.action.DEVICE_ADMIN_DISABLED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
     field public static final java.lang.String ACTION_DEVICE_ADMIN_ENABLED = "android.app.action.DEVICE_ADMIN_ENABLED";
+    field public static final java.lang.String ACTION_LOCK_TASK_CHANGED = "android.app.action.ACTION_LOCK_TASK_CHANGED";
     field public static final java.lang.String ACTION_PASSWORD_CHANGED = "android.app.action.ACTION_PASSWORD_CHANGED";
     field public static final java.lang.String ACTION_PASSWORD_EXPIRING = "android.app.action.ACTION_PASSWORD_EXPIRING";
     field public static final java.lang.String ACTION_PASSWORD_FAILED = "android.app.action.ACTION_PASSWORD_FAILED";
@@ -5193,6 +5228,8 @@
     field public static final java.lang.String ACTION_PROFILE_PROVISIONING_COMPLETE = "android.app.action.ACTION_PROFILE_PROVISIONING_COMPLETE";
     field public static final java.lang.String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
     field public static final java.lang.String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
+    field public static final java.lang.String EXTRA_LOCK_TASK_ENTERING = "android.app.extra.LOCK_TASK_ENTERING";
+    field public static final java.lang.String EXTRA_LOCK_TASK_PACKAGE = "android.app.extra.LOCK_TASK_PACKAGE";
   }
 
   public class DevicePolicyManager {
@@ -5200,7 +5237,7 @@
     method public void addPersistentPreferredActivity(android.content.ComponentName, android.content.IntentFilter, android.content.ComponentName);
     method public void addUserRestriction(android.content.ComponentName, java.lang.String);
     method public void clearCrossProfileIntentFilters(android.content.ComponentName);
-    method public void clearDeviceOwnerApp();
+    method public void clearDeviceOwnerApp(java.lang.String);
     method public void clearPackagePersistentPreferredActivities(android.content.ComponentName, java.lang.String);
     method public void clearUserRestriction(android.content.ComponentName, java.lang.String);
     method public android.os.UserHandle createAndInitializeUser(android.content.ComponentName, java.lang.String, java.lang.String, android.content.ComponentName, android.os.Bundle);
@@ -5235,7 +5272,7 @@
     method public boolean isAdminActive(android.content.ComponentName);
     method public boolean isApplicationBlocked(android.content.ComponentName, java.lang.String);
     method public boolean isDeviceOwnerApp(java.lang.String);
-    method public boolean isLockTaskPermitted(android.content.ComponentName);
+    method public boolean isLockTaskPermitted(java.lang.String);
     method public boolean isMasterVolumeMuted(android.content.ComponentName);
     method public boolean isProfileOwnerApp(java.lang.String);
     method public void lockNow();
@@ -5249,7 +5286,7 @@
     method public void setCameraDisabled(android.content.ComponentName, boolean);
     method public void setGlobalSetting(android.content.ComponentName, java.lang.String, java.lang.String);
     method public void setKeyguardDisabledFeatures(android.content.ComponentName, int);
-    method public void setLockTaskComponents(android.content.ComponentName[]) throws java.lang.SecurityException;
+    method public void setLockTaskPackages(java.lang.String[]) throws java.lang.SecurityException;
     method public void setMasterVolumeMuted(android.content.ComponentName, boolean);
     method public void setMaximumFailedPasswordsForWipe(android.content.ComponentName, int);
     method public void setMaximumTimeToLock(android.content.ComponentName, long);
@@ -8049,6 +8086,7 @@
     field public static final int DOCUMENT_LAUNCH_INTO_EXISTING = 1; // 0x1
     field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3
     field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0
+    field public static final int DO_NOT_PERSIST = 1; // 0x1
     field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40
     field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8
     field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000
@@ -8060,13 +8098,15 @@
     field public static final int FLAG_IMMERSIVE = 2048; // 0x800
     field public static final int FLAG_MULTIPROCESS = 1; // 0x1
     field public static final int FLAG_NO_HISTORY = 128; // 0x80
-    field public static final int FLAG_PERSISTABLE = 4096; // 0x1000
+    field public static final int FLAG_RELINQUISH_TASK_IDENTITY = 4096; // 0x1000
     field public static final int FLAG_SINGLE_USER = 1073741824; // 0x40000000
     field public static final int FLAG_STATE_NOT_NEEDED = 16; // 0x10
     field public static final int LAUNCH_MULTIPLE = 0; // 0x0
     field public static final int LAUNCH_SINGLE_INSTANCE = 3; // 0x3
     field public static final int LAUNCH_SINGLE_TASK = 2; // 0x2
     field public static final int LAUNCH_SINGLE_TOP = 1; // 0x1
+    field public static final int PERSIST_ACROSS_REBOOTS = 2; // 0x2
+    field public static final int PERSIST_ROOT_ONLY = 0; // 0x0
     field public static final int SCREEN_ORIENTATION_BEHIND = 3; // 0x3
     field public static final int SCREEN_ORIENTATION_FULL_SENSOR = 10; // 0xa
     field public static final int SCREEN_ORIENTATION_FULL_USER = 13; // 0xd
@@ -8091,6 +8131,7 @@
     field public int maxRecents;
     field public java.lang.String parentActivityName;
     field public java.lang.String permission;
+    field public int persistableMode;
     field public int screenOrientation;
     field public int softInputMode;
     field public java.lang.String targetActivity;
@@ -8112,6 +8153,7 @@
     field public static final int FLAG_DEBUGGABLE = 2; // 0x2
     field public static final int FLAG_EXTERNAL_STORAGE = 262144; // 0x40000
     field public static final int FLAG_FACTORY_TEST = 16; // 0x10
+    field public static final int FLAG_FULL_BACKUP_ONLY = 67108864; // 0x4000000
     field public static final int FLAG_HAS_CODE = 4; // 0x4
     field public static final int FLAG_INSTALLED = 8388608; // 0x800000
     field public static final int FLAG_IS_DATA_ONLY = 16777216; // 0x1000000
@@ -8148,6 +8190,8 @@
     field public int requiresSmallestWidthDp;
     field public java.lang.String[] sharedLibraryFiles;
     field public java.lang.String sourceDir;
+    field public java.lang.String[] splitPublicSourceDirs;
+    field public java.lang.String[] splitSourceDirs;
     field public int targetSdkVersion;
     field public java.lang.String taskAffinity;
     field public int theme;
@@ -8216,6 +8260,8 @@
     field public boolean handleProfiling;
     field public java.lang.String publicSourceDir;
     field public java.lang.String sourceDir;
+    field public java.lang.String[] splitPublicSourceDirs;
+    field public java.lang.String[] splitSourceDirs;
     field public java.lang.String targetPackage;
   }
 
@@ -8245,15 +8291,26 @@
   }
 
   public class LauncherApps {
+    method public void addOnAppsChangedCallback(android.content.pm.LauncherApps.OnAppsChangedCallback);
     method public void addOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
     method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
     method public boolean isActivityEnabledForProfile(android.content.ComponentName, android.os.UserHandle);
     method public boolean isPackageEnabledForProfile(java.lang.String, android.os.UserHandle);
+    method public void removeOnAppsChangedCallback(android.content.pm.LauncherApps.OnAppsChangedCallback);
     method public void removeOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
     method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
     method public void startActivityForProfile(android.content.ComponentName, android.os.UserHandle, android.graphics.Rect, android.os.Bundle);
   }
 
+  public static abstract class LauncherApps.OnAppsChangedCallback {
+    ctor public LauncherApps.OnAppsChangedCallback();
+    method public abstract void onPackageAdded(java.lang.String, android.os.UserHandle);
+    method public abstract void onPackageChanged(java.lang.String, android.os.UserHandle);
+    method public abstract void onPackageRemoved(java.lang.String, android.os.UserHandle);
+    method public abstract void onPackagesAvailable(java.lang.String[], android.os.UserHandle, boolean);
+    method public abstract void onPackagesUnavailable(java.lang.String[], android.os.UserHandle, boolean);
+  }
+
   public static abstract interface LauncherApps.OnAppsChangedListener {
     method public abstract void onPackageAdded(android.os.UserHandle, java.lang.String);
     method public abstract void onPackageChanged(android.os.UserHandle, java.lang.String);
@@ -11201,6 +11258,17 @@
     method public void addTransition(int, int, android.graphics.drawable.AnimationDrawable, boolean);
   }
 
+  public class AnimatedVectorDrawable extends android.graphics.drawable.Drawable implements android.graphics.drawable.Animatable {
+    ctor public AnimatedVectorDrawable();
+    method public void draw(android.graphics.Canvas);
+    method public int getOpacity();
+    method public boolean isRunning();
+    method public void setAlpha(int);
+    method public void setColorFilter(android.graphics.ColorFilter);
+    method public void start();
+    method public void stop();
+  }
+
   public class AnimationDrawable extends android.graphics.drawable.DrawableContainer implements android.graphics.drawable.Animatable java.lang.Runnable {
     ctor public AnimationDrawable();
     method public void addFrame(android.graphics.drawable.Drawable, int);
@@ -11600,8 +11668,6 @@
     method public int getOpacity();
     method public void setAlpha(int);
     method public void setColorFilter(android.graphics.ColorFilter);
-    method public void setPadding(android.graphics.Rect);
-    method public void setPadding(int, int, int, int);
   }
 
 }
@@ -12791,6 +12857,7 @@
     method public boolean isOutputSupportedFor(int);
     method public static boolean isOutputSupportedFor(java.lang.Class<T>);
     method public boolean isOutputSupportedFor(android.view.Surface);
+    field public static final long NO_MIN_FRAME_DURATION = 0L; // 0x0L
   }
 
   public final class TonemapCurve {
@@ -13698,7 +13765,9 @@
     field public static final int CHANNEL_OUT_QUAD = 204; // 0xcc
     field public static final int CHANNEL_OUT_STEREO = 12; // 0xc
     field public static final int CHANNEL_OUT_SURROUND = 1052; // 0x41c
+    field public static final int ENCODING_AC3 = 5; // 0x5
     field public static final int ENCODING_DEFAULT = 1; // 0x1
+    field public static final int ENCODING_E_AC3 = 6; // 0x6
     field public static final int ENCODING_INVALID = 0; // 0x0
     field public static final int ENCODING_PCM_16BIT = 2; // 0x2
     field public static final int ENCODING_PCM_8BIT = 3; // 0x3
@@ -13750,6 +13819,7 @@
     method public deprecated void setWiredHeadsetOn(boolean);
     method public deprecated boolean shouldVibrate(int);
     method public void startBluetoothSco();
+    method public void startBluetoothScoVirtualCall();
     method public void stopBluetoothSco();
     method public void unloadSoundEffects();
     method public void unregisterMediaButtonEventReceiver(android.content.ComponentName);
@@ -13772,6 +13842,7 @@
     field public static final int AUDIOFOCUS_REQUEST_FAILED = 0; // 0x0
     field public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; // 0x1
     field public static final int ERROR = -1; // 0xffffffff
+    field public static final int ERROR_DEAD_OBJECT = -6; // 0xfffffffa
     field public static final java.lang.String EXTRA_RINGER_MODE = "android.media.EXTRA_RINGER_MODE";
     field public static final java.lang.String EXTRA_SCO_AUDIO_PREVIOUS_STATE = "android.media.extra.SCO_AUDIO_PREVIOUS_STATE";
     field public static final java.lang.String EXTRA_SCO_AUDIO_STATE = "android.media.extra.SCO_AUDIO_STATE";
@@ -14433,6 +14504,11 @@
     method public java.lang.String getDefaultUrl();
   }
 
+  public static final class MediaDrm.MediaDrmStateException extends java.lang.IllegalStateException {
+    ctor public MediaDrm.MediaDrmStateException(int, java.lang.String);
+    method public int getErrorCode();
+  }
+
   public static abstract interface MediaDrm.OnEventListener {
     method public abstract void onEvent(android.media.MediaDrm, byte[], int, int, byte[]);
   }
@@ -15301,6 +15377,19 @@
     ctor public UnsupportedSchemeException(java.lang.String);
   }
 
+  public abstract class VolumeProvider {
+    ctor public VolumeProvider(int, int);
+    method public final int getMaxVolume();
+    method public final int getVolumeControl();
+    method public final void notifyVolumeChanged();
+    method public void onAdjustVolumeBy(int);
+    method public abstract int onGetCurrentVolume();
+    method public void onSetVolumeTo(int);
+    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
+    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
+    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
+  }
+
 }
 
 package android.media.audiofx {
@@ -15667,6 +15756,7 @@
     method public android.media.session.PlaybackState getPlaybackState();
     method public int getRatingType();
     method public android.media.session.MediaController.TransportControls getTransportControls();
+    method public android.media.session.MediaController.VolumeInfo getVolumeInfo();
     method public void removeCallback(android.media.session.MediaController.Callback);
     method public void sendControlCommand(java.lang.String, android.os.Bundle, android.os.ResultReceiver);
   }
@@ -15690,6 +15780,14 @@
     method public void stop();
   }
 
+  public static final class MediaController.VolumeInfo {
+    method public int getAudioStream();
+    method public int getCurrentVolume();
+    method public int getMaxVolume();
+    method public int getVolumeControl();
+    method public int getVolumeType();
+  }
+
   public final class MediaSession {
     method public void addCallback(android.media.session.MediaSession.Callback);
     method public void addCallback(android.media.session.MediaSession.Callback, android.os.Handler);
@@ -15707,9 +15805,11 @@
     method public void setMetadata(android.media.MediaMetadata);
     method public void setPlaybackState(android.media.session.PlaybackState);
     method public void setPlaybackToLocal(int);
-    method public void setPlaybackToRemote(android.media.session.RemoteVolumeProvider);
+    method public void setPlaybackToRemote(android.media.VolumeProvider);
     field public static final int FLAG_HANDLES_MEDIA_BUTTONS = 1; // 0x1
     field public static final int FLAG_HANDLES_TRANSPORT_CONTROLS = 2; // 0x2
+    field public static final int VOLUME_TYPE_LOCAL = 1; // 0x1
+    field public static final int VOLUME_TYPE_REMOTE = 2; // 0x2
   }
 
   public static abstract class MediaSession.Callback {
@@ -15732,7 +15832,14 @@
   }
 
   public final class MediaSessionManager {
+    method public void addActiveSessionsListener(android.media.session.MediaSessionManager.SessionListener, android.content.ComponentName);
     method public android.media.session.MediaSession createSession(java.lang.String);
+    method public java.util.List<android.media.session.MediaController> getActiveSessions(android.content.ComponentName);
+  }
+
+  public static abstract class MediaSessionManager.SessionListener {
+    ctor public MediaSessionManager.SessionListener();
+    method public abstract void onActiveSessionsChanged(java.util.List<android.media.session.MediaController>);
   }
 
   public final class MediaSessionToken implements android.os.Parcelable {
@@ -15780,19 +15887,6 @@
     field public static final int STATE_STOPPED = 1; // 0x1
   }
 
-  public abstract class RemoteVolumeProvider {
-    ctor public RemoteVolumeProvider(int, int);
-    method public final int getMaxVolume();
-    method public final int getVolumeControl();
-    method public final void notifyVolumeChanged();
-    method public void onAdjustVolumeBy(int);
-    method public abstract int onGetCurrentVolume();
-    method public void onSetVolumeTo(int);
-    field public static final int VOLUME_CONTROL_ABSOLUTE = 2; // 0x2
-    field public static final int VOLUME_CONTROL_FIXED = 0; // 0x0
-    field public static final int VOLUME_CONTROL_RELATIVE = 1; // 0x1
-  }
-
 }
 
 package android.media.tv {
@@ -15816,6 +15910,7 @@
   }
 
   public static final class TvContract.Channels implements android.media.tv.TvContract.BaseTvColumns {
+    method public static final java.lang.String getVideoResolution(java.lang.String);
     field public static final java.lang.String COLUMN_BROWSABLE = "browsable";
     field public static final java.lang.String COLUMN_DESCRIPTION = "description";
     field public static final java.lang.String COLUMN_DISPLAY_NAME = "display_name";
@@ -15829,6 +15924,7 @@
     field public static final java.lang.String COLUMN_TRANSPORT_STREAM_ID = "transport_stream_id";
     field public static final java.lang.String COLUMN_TYPE = "type";
     field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_FORMAT = "video_format";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/channel";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/channel";
     field public static final android.net.Uri CONTENT_URI;
@@ -15860,6 +15956,22 @@
     field public static final int TYPE_SECAM = 3; // 0x3
     field public static final int TYPE_S_DMB = 393472; // 0x60100
     field public static final int TYPE_T_DMB = 393216; // 0x60000
+    field public static final java.lang.String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
+    field public static final java.lang.String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
+    field public static final java.lang.String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
+    field public static final java.lang.String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
+    field public static final java.lang.String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P";
+    field public static final java.lang.String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P";
+    field public static final java.lang.String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I";
+    field public static final java.lang.String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P";
+    field public static final java.lang.String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I";
+    field public static final java.lang.String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P";
+    field public static final java.lang.String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P";
+    field public static final java.lang.String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED";
+    field public static final java.lang.String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD";
+    field public static final java.lang.String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD";
+    field public static final java.lang.String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD";
+    field public static final java.lang.String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD";
   }
 
   public static final class TvContract.Channels.Logo {
@@ -15880,6 +15992,8 @@
     field public static final java.lang.String COLUMN_THUMBNAIL_URI = "thumbnail_uri";
     field public static final java.lang.String COLUMN_TITLE = "title";
     field public static final java.lang.String COLUMN_VERSION_NUMBER = "version_number";
+    field public static final java.lang.String COLUMN_VIDEO_HEIGHT = "video_height";
+    field public static final java.lang.String COLUMN_VIDEO_WIDTH = "video_width";
     field public static final java.lang.String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/program";
     field public static final java.lang.String CONTENT_TYPE = "vnd.android.cursor.dir/program";
     field public static final android.net.Uri CONTENT_URI;
@@ -15888,17 +16002,17 @@
   public static final class TvContract.Programs.Genres {
     method public static java.lang.String[] decode(java.lang.String);
     method public static java.lang.String encode(java.lang.String...);
-    field public static final java.lang.String ANIMAL_WILDLIFE = "Animal/Wildlife";
-    field public static final java.lang.String COMEDY = "Comedy";
-    field public static final java.lang.String DRAMA = "Drama";
-    field public static final java.lang.String EDUCATION = "Education";
-    field public static final java.lang.String FAMILY_KIDS = "Family/Kids";
-    field public static final java.lang.String GAMING = "Gaming";
-    field public static final java.lang.String MOVIES = "Movies";
-    field public static final java.lang.String NEWS = "News";
-    field public static final java.lang.String SHOPPING = "Shopping";
-    field public static final java.lang.String SPORTS = "Sports";
-    field public static final java.lang.String TRAVEL = "Travel";
+    field public static final java.lang.String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE";
+    field public static final java.lang.String COMEDY = "COMEDY";
+    field public static final java.lang.String DRAMA = "DRAMA";
+    field public static final java.lang.String EDUCATION = "EDUCATION";
+    field public static final java.lang.String FAMILY_KIDS = "FAMILY_KIDS";
+    field public static final java.lang.String GAMING = "GAMING";
+    field public static final java.lang.String MOVIES = "MOVIES";
+    field public static final java.lang.String NEWS = "NEWS";
+    field public static final java.lang.String SHOPPING = "SHOPPING";
+    field public static final java.lang.String SPORTS = "SPORTS";
+    field public static final java.lang.String TRAVEL = "TRAVEL";
   }
 
   public final class TvInputInfo implements android.os.Parcelable {
@@ -17571,6 +17685,7 @@
     method public boolean invokeBeam(android.app.Activity);
     method public boolean isEnabled();
     method public boolean isNdefPushEnabled();
+    method public boolean registerLockscreenDispatch(android.nfc.NfcAdapter.NfcLockscreenDispatch, java.lang.String[]);
     method public void setBeamPushUris(android.net.Uri[], android.app.Activity);
     method public void setBeamPushUrisCallback(android.nfc.NfcAdapter.CreateBeamUrisCallback, android.app.Activity);
     method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...);
@@ -17606,6 +17721,10 @@
     method public abstract android.nfc.NdefMessage createNdefMessage(android.nfc.NfcEvent);
   }
 
+  public static abstract interface NfcAdapter.NfcLockscreenDispatch {
+    method public abstract boolean onTagDetected(android.nfc.Tag);
+  }
+
   public static abstract interface NfcAdapter.OnNdefPushCompleteCallback {
     method public abstract void onNdefPushComplete(android.nfc.NfcEvent);
   }
@@ -21529,6 +21648,7 @@
   public class UserManager {
     method public android.os.Bundle getApplicationRestrictions(java.lang.String);
     method public android.graphics.drawable.Drawable getBadgedDrawableForUser(android.graphics.drawable.Drawable, android.os.UserHandle);
+    method public java.lang.String getBadgedLabelForUser(java.lang.String, android.os.UserHandle);
     method public long getSerialNumberForUser(android.os.UserHandle);
     method public int getUserCount();
     method public android.os.UserHandle getUserForSerialNumber(long);
@@ -21546,7 +21666,7 @@
     method public void setUserRestrictions(android.os.Bundle, android.os.UserHandle);
     field public static final java.lang.String DISALLOW_ADD_USER = "no_add_user";
     field public static final java.lang.String DISALLOW_ADJUST_VOLUME = "no_adjust_volume";
-    field public static final java.lang.String DISALLOW_CONFIG_APPS = "no_config_apps";
+    field public static final java.lang.String DISALLOW_APPS_CONTROL = "no_control_apps";
     field public static final java.lang.String DISALLOW_CONFIG_BLUETOOTH = "no_config_bluetooth";
     field public static final java.lang.String DISALLOW_CONFIG_CELL_BROADCASTS = "no_config_cell_broadcasts";
     field public static final java.lang.String DISALLOW_CONFIG_CREDENTIALS = "no_config_credentials";
@@ -23350,6 +23470,7 @@
   protected static abstract interface ContactsContract.ContactOptionsColumns {
     field public static final java.lang.String CUSTOM_RINGTONE = "custom_ringtone";
     field public static final java.lang.String LAST_TIME_CONTACTED = "last_time_contacted";
+    field public static final java.lang.String PINNED = "pinned";
     field public static final java.lang.String SEND_TO_VOICEMAIL = "send_to_voicemail";
     field public static final java.lang.String STARRED = "starred";
     field public static final java.lang.String TIMES_CONTACTED = "times_contacted";
@@ -23393,7 +23514,7 @@
     field public static final java.lang.String CONTENT_DIRECTORY = "data";
   }
 
-  public static final class ContactsContract.Contacts.Entity implements android.provider.BaseColumns android.provider.ContactsContract.BaseSyncColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.StatusColumns android.provider.ContactsContract.SyncColumns {
+  public static final class ContactsContract.Contacts.Entity implements android.provider.BaseColumns android.provider.ContactsContract.BaseSyncColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.DataUsageStatColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.StatusColumns android.provider.ContactsContract.SyncColumns {
     field public static final java.lang.String CONTENT_DIRECTORY = "entities";
     field public static final java.lang.String DATA_ID = "data_id";
     field public static final java.lang.String RAW_CONTACT_ID = "raw_contact_id";
@@ -23627,6 +23748,13 @@
     field public static final int UNDEFINED = 0; // 0x0
   }
 
+  public static final class ContactsContract.PinnedPositions {
+    ctor public ContactsContract.PinnedPositions();
+    field public static final int DEMOTED = -1; // 0xffffffff
+    field public static final java.lang.String UNDEMOTE_METHOD = "undemote";
+    field public static final int UNPINNED = 2147483647; // 0x7fffffff
+  }
+
   public static final class ContactsContract.Preferences {
     ctor public ContactsContract.Preferences();
     field public static final java.lang.String DISPLAY_ORDER = "android.contacts.DISPLAY_ORDER";
@@ -23669,6 +23797,8 @@
     ctor public ContactsContract.QuickContact();
     method public static void showQuickContact(android.content.Context, android.view.View, android.net.Uri, int, java.lang.String[]);
     method public static void showQuickContact(android.content.Context, android.graphics.Rect, android.net.Uri, int, java.lang.String[]);
+    field public static final java.lang.String ACTION_QUICK_CONTACT = "com.android.contacts.action.QUICK_CONTACT";
+    field public static final java.lang.String EXTRA_EXCLUDE_MIMES = "exclude_mimes";
     field public static final int MODE_LARGE = 3; // 0x3
     field public static final int MODE_MEDIUM = 2; // 0x2
     field public static final int MODE_SMALL = 1; // 0x1
@@ -24431,6 +24561,7 @@
     field public static final java.lang.String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
     field public static final java.lang.String SELECTED_INPUT_METHOD_SUBTYPE = "selected_input_method_subtype";
     field public static final java.lang.String SETTINGS_CLASSNAME = "settings_classname";
+    field public static final java.lang.String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
     field public static final java.lang.String SYS_PROP_SETTING_VERSION = "sys.settings_secure_version";
     field public static final java.lang.String TOUCH_EXPLORATION_ENABLED = "touch_exploration_enabled";
     field public static final deprecated java.lang.String TTS_DEFAULT_COUNTRY = "tts_default_country";
@@ -26020,7 +26151,6 @@
 package android.service.fingerprint {
 
   public class FingerprintManager {
-    ctor public FingerprintManager(android.content.Context, android.service.fingerprint.IFingerprintService);
     method public void enroll(long);
     method public void enrollCancel();
     method public boolean enrolledAndEnabled();
@@ -26061,22 +26191,6 @@
     method public static boolean removeFingerprintIdForUser(int, android.content.ContentResolver, int);
   }
 
-  public abstract interface IFingerprintService implements android.os.IInterface {
-    method public abstract void enroll(android.os.IBinder, long, int) throws android.os.RemoteException;
-    method public abstract void enrollCancel(android.os.IBinder, int) throws android.os.RemoteException;
-    method public abstract void remove(android.os.IBinder, int, int) throws android.os.RemoteException;
-    method public abstract void startListening(android.os.IBinder, android.service.fingerprint.IFingerprintServiceReceiver, int) throws android.os.RemoteException;
-    method public abstract void stopListening(android.os.IBinder, int) throws android.os.RemoteException;
-  }
-
-  public abstract interface IFingerprintServiceReceiver implements android.os.IInterface {
-    method public abstract void onAcquired(int) throws android.os.RemoteException;
-    method public abstract void onEnrollResult(int, int) throws android.os.RemoteException;
-    method public abstract void onError(int) throws android.os.RemoteException;
-    method public abstract void onProcessed(int) throws android.os.RemoteException;
-    method public abstract void onRemoved(int) throws android.os.RemoteException;
-  }
-
 }
 
 package android.service.notification {
@@ -27469,7 +27583,6 @@
     method protected final android.telecomm.CallServiceAdapter getAdapter();
     method public final android.os.IBinder getBinder();
     method public abstract void hold(java.lang.String);
-    method public abstract void isCompatibleWith(android.telecomm.CallInfo);
     method protected void onAdapterAttached(android.telecomm.CallServiceAdapter);
     method public abstract void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState);
     method public final android.os.IBinder onBind(android.content.Intent);
@@ -27492,7 +27605,6 @@
     method public void setActive(java.lang.String);
     method public void setDialing(java.lang.String);
     method public void setDisconnected(java.lang.String, int, java.lang.String);
-    method public void setIsCompatibleWith(java.lang.String, boolean);
     method public void setOnHold(java.lang.String);
     method public void setRequestingRingback(java.lang.String, boolean);
     method public void setRinging(java.lang.String);
@@ -27527,22 +27639,6 @@
     method public android.os.IBinder onBind(android.content.Intent);
   }
 
-  public abstract class CallServiceSelector extends android.app.Service {
-    ctor protected CallServiceSelector();
-    method protected final void cancelOutgoingCall(android.telecomm.CallInfo);
-    method protected final android.telecomm.CallServiceSelectorAdapter getAdapter();
-    method protected final java.util.Collection<android.telecomm.CallInfo> getCalls();
-    method protected void onAdapterAttached(android.telecomm.CallServiceSelectorAdapter);
-    method public final android.os.IBinder onBind(android.content.Intent);
-    method protected abstract void select(android.telecomm.CallInfo, java.util.List<android.telecomm.CallServiceDescriptor>);
-  }
-
-  public final class CallServiceSelectorAdapter {
-    method public void cancelOutgoingCall(java.lang.String);
-    method public void setHandoffInfo(java.lang.String, android.net.Uri, android.os.Bundle);
-    method public void setSelectedCallServices(java.lang.String, java.util.List<android.telecomm.CallServiceDescriptor>);
-  }
-
   public final class CallState extends java.lang.Enum {
     method public static android.telecomm.CallState valueOf(java.lang.String);
     method public static final android.telecomm.CallState[] values();
@@ -27563,6 +27659,7 @@
     method public java.util.List<android.telecomm.Connection> getChildConnections();
     method public final android.net.Uri getHandle();
     method public android.telecomm.Connection getParentConnection();
+    method public boolean isConferenceCapable();
     method public boolean isConferenceConnection();
     method public boolean isRequestingRingback();
     method protected void onAbort();
@@ -27647,14 +27744,12 @@
     method public final void disconnect(java.lang.String);
     method public java.util.Collection<android.telecomm.Connection> getAllConnections();
     method public final void hold(java.lang.String);
-    method public final void isCompatibleWith(android.telecomm.CallInfo);
     method public final void onAudioStateChanged(java.lang.String, android.telecomm.CallAudioState);
     method public void onConnectionAdded(android.telecomm.Connection);
     method public void onConnectionRemoved(android.telecomm.Connection);
     method public void onCreateConferenceConnection(java.lang.String, android.telecomm.Connection, android.telecomm.Response<java.lang.String, android.telecomm.Connection>);
     method public void onCreateConnections(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
     method public void onCreateIncomingConnection(android.telecomm.ConnectionRequest, android.telecomm.Response<android.telecomm.ConnectionRequest, android.telecomm.Connection>);
-    method public void onFindSubscriptions(android.net.Uri, android.telecomm.Response<android.net.Uri, android.telecomm.Subscription>);
     method public final void onPostDialContinue(java.lang.String, boolean);
     method public final void onPostDialWait(android.telecomm.Connection, java.lang.String);
     method public final void playDtmfTone(java.lang.String, char);
@@ -27724,8 +27819,17 @@
   }
 
   public class Subscription implements android.os.Parcelable {
-    ctor public Subscription();
+    ctor public Subscription(android.content.ComponentName, java.lang.String, android.net.Uri, int, int, int, boolean, boolean);
     method public int describeContents();
+    method public android.content.ComponentName getComponentName();
+    method public android.net.Uri getHandle();
+    method public android.graphics.drawable.Drawable getIcon(android.content.Context);
+    method public android.graphics.drawable.Drawable getIcon(android.content.Context, int);
+    method public java.lang.String getId();
+    method public java.lang.String getLabel(android.content.Context);
+    method public java.lang.String getShortDescription(android.content.Context);
+    method public boolean isEnabled();
+    method public boolean isSystemDefault();
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
   }
@@ -27734,8 +27838,6 @@
     ctor public TelecommConstants();
     field public static final java.lang.String ACTION_CALL_SERVICE;
     field public static final java.lang.String ACTION_CALL_SERVICE_PROVIDER;
-    field public static final java.lang.String ACTION_CALL_SERVICE_SELECTOR;
-    field public static final java.lang.String ACTION_CHANGE_DEFAULT_PHONE = "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
     field public static final java.lang.String ACTION_INCOMING_CALL = "android.intent.action.INCOMING_CALL";
     field public static final char DTMF_CHARACTER_PAUSE = 44; // 0x002c ','
     field public static final char DTMF_CHARACTER_WAIT = 59; // 0x003b ';'
@@ -27743,7 +27845,6 @@
     field public static final java.lang.String EXTRA_CALL_DISCONNECT_MESSAGE = "android.telecomm.extra.CALL_DISCONNECT_MESSAGE";
     field public static final java.lang.String EXTRA_CALL_SERVICE_DESCRIPTOR = "android.intent.extra.CALL_SERVICE_DESCRIPTOR";
     field public static final java.lang.String EXTRA_INCOMING_CALL_EXTRAS = "android.intent.extra.INCOMING_CALL_EXTRAS";
-    field public static final java.lang.String EXTRA_PACKAGE_NAME = "package";
     field public static final java.lang.String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.intent.extra.START_CALL_WITH_SPEAKERPHONE";
   }
 
@@ -28189,6 +28290,7 @@
     method public java.lang.String getSimSerialNumber();
     method public int getSimState();
     method public java.lang.String getSubscriberId();
+    method public java.util.List<android.telecomm.Subscription> getSubscriptions();
     method public java.lang.String getVoiceMailAlphaTag();
     method public java.lang.String getVoiceMailNumber();
     method public boolean hasIccCard();
@@ -28213,6 +28315,7 @@
     field public static final java.lang.String EXTRA_STATE_IDLE;
     field public static final java.lang.String EXTRA_STATE_OFFHOOK;
     field public static final java.lang.String EXTRA_STATE_RINGING;
+    field public static final java.lang.String EXTRA_SUBSCRIPTION = "subscription";
     field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
     field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
     field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
@@ -30444,8 +30547,8 @@
     method public android.transition.TransitionPropagation getPropagation();
     method public long getStartDelay();
     method public java.util.List<java.lang.Integer> getTargetIds();
+    method public java.util.List<java.lang.String> getTargetNames();
     method public java.util.List<java.lang.Class> getTargetTypes();
-    method public java.util.List<java.lang.String> getTargetViewNames();
     method public java.util.List<android.view.View> getTargets();
     method public java.lang.String[] getTransitionProperties();
     method public android.transition.TransitionValues getTransitionValues(android.view.View, boolean);
@@ -30463,7 +30566,7 @@
     field public static final int MATCH_ID = 3; // 0x3
     field public static final int MATCH_INSTANCE = 1; // 0x1
     field public static final int MATCH_ITEM_ID = 4; // 0x4
-    field public static final int MATCH_VIEW_NAME = 2; // 0x2
+    field public static final int MATCH_NAME = 2; // 0x2
   }
 
   public static abstract class Transition.EpicenterCallback {
@@ -31293,6 +31396,7 @@
   }
 
   public final class Display {
+    method public long getAppVsyncOffsetNanos();
     method public void getCurrentSizeRange(android.graphics.Point, android.graphics.Point);
     method public int getDisplayId();
     method public int getFlags();
@@ -31301,6 +31405,7 @@
     method public java.lang.String getName();
     method public deprecated int getOrientation();
     method public deprecated int getPixelFormat();
+    method public long getPresentationDeadlineNanos();
     method public void getRealMetrics(android.util.DisplayMetrics);
     method public void getRealSize(android.graphics.Point);
     method public void getRectSize(android.graphics.Rect);
@@ -32533,6 +32638,7 @@
     method public void dispatchWindowSystemUiVisiblityChanged(int);
     method public void dispatchWindowVisibilityChanged(int);
     method public void draw(android.graphics.Canvas);
+    method public void drawableHotspotChanged(float, float);
     method protected void drawableStateChanged();
     method public android.view.View findFocus();
     method public final android.view.View findViewById(int);
@@ -32548,6 +32654,8 @@
     method public android.view.animation.Animation getAnimation();
     method public android.os.IBinder getApplicationWindowToken();
     method public android.graphics.drawable.Drawable getBackground();
+    method public android.content.res.ColorStateList getBackgroundTint();
+    method public android.graphics.PorterDuff.Mode getBackgroundTintMode();
     method public int getBaseline();
     method public final int getBottom();
     method protected float getBottomFadingEdgeStrength();
@@ -32649,13 +32757,13 @@
     method protected int getTopPaddingOffset();
     method public android.view.TouchDelegate getTouchDelegate();
     method public java.util.ArrayList<android.view.View> getTouchables();
+    method public java.lang.String getTransitionName();
     method public float getTranslationX();
     method public float getTranslationY();
     method public float getTranslationZ();
     method public int getVerticalFadingEdgeLength();
     method public int getVerticalScrollbarPosition();
     method public int getVerticalScrollbarWidth();
-    method public java.lang.String getViewName();
     method public android.view.ViewTreeObserver getViewTreeObserver();
     method public int getVisibility();
     method public final int getWidth();
@@ -32825,6 +32933,8 @@
     method public void setBackgroundColor(int);
     method public deprecated void setBackgroundDrawable(android.graphics.drawable.Drawable);
     method public void setBackgroundResource(int);
+    method public void setBackgroundTint(android.content.res.ColorStateList);
+    method public void setBackgroundTintMode(android.graphics.PorterDuff.Mode);
     method public final void setBottom(int);
     method public void setCameraDistance(float);
     method public void setClickable(boolean);
@@ -32910,13 +33020,13 @@
     method public void setTextDirection(int);
     method public final void setTop(int);
     method public void setTouchDelegate(android.view.TouchDelegate);
+    method public final void setTransitionName(java.lang.String);
     method public void setTranslationX(float);
     method public void setTranslationY(float);
     method public void setTranslationZ(float);
     method public void setVerticalFadingEdgeEnabled(boolean);
     method public void setVerticalScrollBarEnabled(boolean);
     method public void setVerticalScrollbarPosition(int);
-    method public final void setViewName(java.lang.String);
     method public void setVisibility(int);
     method public void setWillNotCacheDrawing(boolean);
     method public void setWillNotDraw(boolean);
@@ -35185,6 +35295,7 @@
     method public abstract long getResources();
     method public abstract void grant(long);
     field public static final long RESOURCE_AUDIO_CAPTURE = 4L; // 0x4L
+    field public static final long RESOURCE_PROTECTED_MEDIA_ID = 8L; // 0x8L
     field public static final long RESOURCE_VIDEO_CAPTURE = 2L; // 0x2L
   }
 
@@ -35791,10 +35902,14 @@
     method public boolean getSplitTrack();
     method public android.graphics.drawable.Drawable getThumb();
     method public int getThumbOffset();
+    method public android.content.res.ColorStateList getThumbTint();
+    method public android.graphics.PorterDuff.Mode getThumbTintMode();
     method public void setKeyProgressIncrement(int);
     method public void setSplitTrack(boolean);
     method public void setThumb(android.graphics.drawable.Drawable);
     method public void setThumbOffset(int);
+    method public void setThumbTint(android.content.res.ColorStateList);
+    method public void setThumbTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public abstract class AbsSpinner extends android.widget.AdapterView {
@@ -36209,9 +36324,13 @@
     ctor public CompoundButton(android.content.Context, android.util.AttributeSet);
     ctor public CompoundButton(android.content.Context, android.util.AttributeSet, int);
     ctor public CompoundButton(android.content.Context, android.util.AttributeSet, int, int);
+    method public android.content.res.ColorStateList getButtonTint();
+    method public android.graphics.PorterDuff.Mode getButtonTintMode();
     method public boolean isChecked();
     method public void setButtonDrawable(int);
     method public void setButtonDrawable(android.graphics.drawable.Drawable);
+    method public void setButtonTint(android.content.res.ColorStateList);
+    method public void setButtonTintMode(android.graphics.PorterDuff.Mode);
     method public void setChecked(boolean);
     method public void setOnCheckedChangeListener(android.widget.CompoundButton.OnCheckedChangeListener);
     method public void toggle();
@@ -36333,12 +36452,14 @@
     ctor public EdgeEffect(android.content.Context);
     method public boolean draw(android.graphics.Canvas);
     method public void finish();
+    method public int getColor();
     method public int getMaxHeight();
     method public boolean isFinished();
     method public void onAbsorb(int);
     method public void onPull(float);
     method public void onPull(float, float);
     method public void onRelease();
+    method public void setColor(int);
     method public void setSize(int, int);
   }
 
@@ -36472,10 +36593,14 @@
     method public deprecated boolean getConsiderGoneChildrenWhenMeasuring();
     method public android.graphics.drawable.Drawable getForeground();
     method public int getForegroundGravity();
+    method public android.content.res.ColorStateList getForegroundTint();
+    method public android.graphics.PorterDuff.Mode getForegroundTintMode();
     method public boolean getMeasureAllChildren();
     method protected void onLayout(boolean, int, int, int, int);
     method public void setForeground(android.graphics.drawable.Drawable);
     method public void setForegroundGravity(int);
+    method public void setForegroundTint(android.content.res.ColorStateList);
+    method public void setForegroundTintMode(android.graphics.PorterDuff.Mode);
     method public void setMeasureAllChildren(boolean);
   }
 
@@ -36684,6 +36809,8 @@
     method public int getMaxHeight();
     method public int getMaxWidth();
     method public android.widget.ImageView.ScaleType getScaleType();
+    method public android.content.res.ColorStateList getTint();
+    method public android.graphics.PorterDuff.Mode getTintMode();
     method public int[] onCreateDrawableState(int);
     method public void setAdjustViewBounds(boolean);
     method public deprecated void setAlpha(int);
@@ -36705,6 +36832,8 @@
     method public void setMaxHeight(int);
     method public void setMaxWidth(int);
     method public void setScaleType(android.widget.ImageView.ScaleType);
+    method public void setTint(android.content.res.ColorStateList);
+    method public void setTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public static final class ImageView.ScaleType extends java.lang.Enum {
@@ -37077,11 +37206,19 @@
     ctor public ProgressBar(android.content.Context, android.util.AttributeSet, int);
     ctor public ProgressBar(android.content.Context, android.util.AttributeSet, int, int);
     method public android.graphics.drawable.Drawable getIndeterminateDrawable();
+    method public android.content.res.ColorStateList getIndeterminateTint();
+    method public android.graphics.PorterDuff.Mode getIndeterminateTintMode();
     method public android.view.animation.Interpolator getInterpolator();
     method public synchronized int getMax();
     method public synchronized int getProgress();
+    method public android.content.res.ColorStateList getProgressBackgroundTint();
+    method public android.graphics.PorterDuff.Mode getProgressBackgroundTintMode();
     method public android.graphics.drawable.Drawable getProgressDrawable();
+    method public android.content.res.ColorStateList getProgressTint();
+    method public android.graphics.PorterDuff.Mode getProgressTintMode();
     method public synchronized int getSecondaryProgress();
+    method public android.content.res.ColorStateList getSecondaryProgressTint();
+    method public android.graphics.PorterDuff.Mode getSecondaryProgressTintMode();
     method public final synchronized void incrementProgressBy(int);
     method public final synchronized void incrementSecondaryProgressBy(int);
     method public synchronized boolean isIndeterminate();
@@ -37090,13 +37227,21 @@
     method public synchronized void setIndeterminate(boolean);
     method public void setIndeterminateDrawable(android.graphics.drawable.Drawable);
     method public void setIndeterminateDrawableTiled(android.graphics.drawable.Drawable);
+    method public void setIndeterminateTint(android.content.res.ColorStateList);
+    method public void setIndeterminateTintMode(android.graphics.PorterDuff.Mode);
     method public void setInterpolator(android.content.Context, int);
     method public void setInterpolator(android.view.animation.Interpolator);
     method public synchronized void setMax(int);
     method public synchronized void setProgress(int);
+    method public void setProgressBackgroundTint(android.content.res.ColorStateList);
+    method public void setProgressBackgroundTintMode(android.graphics.PorterDuff.Mode);
     method public void setProgressDrawable(android.graphics.drawable.Drawable);
     method public void setProgressDrawableTiled(android.graphics.drawable.Drawable);
+    method public void setProgressTint(android.content.res.ColorStateList);
+    method public void setProgressTintMode(android.graphics.PorterDuff.Mode);
     method public synchronized void setSecondaryProgress(int);
+    method public void setSecondaryProgressTint(android.content.res.ColorStateList);
+    method public void setSecondaryProgressTintMode(android.graphics.PorterDuff.Mode);
   }
 
   public class QuickContactBadge extends android.widget.ImageView implements android.view.View.OnClickListener {
@@ -38057,9 +38202,11 @@
     method public void setSubtitle(int);
     method public void setSubtitle(java.lang.CharSequence);
     method public void setSubtitleTextAppearance(android.content.Context, int);
+    method public void setSubtitleTextColor(int);
     method public void setTitle(int);
     method public void setTitle(java.lang.CharSequence);
     method public void setTitleTextAppearance(android.content.Context, int);
+    method public void setTitleTextColor(int);
     method public boolean showOverflowMenu();
   }
 
@@ -48148,6 +48295,35 @@
     method public V replace(K, V);
   }
 
+  public class ConcurrentLinkedDeque extends java.util.AbstractCollection implements java.util.Deque java.io.Serializable {
+    ctor public ConcurrentLinkedDeque();
+    ctor public ConcurrentLinkedDeque(java.util.Collection<? extends E>);
+    method public void addFirst(E);
+    method public void addLast(E);
+    method public java.util.Iterator<E> descendingIterator();
+    method public E element();
+    method public E getFirst();
+    method public E getLast();
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E);
+    method public boolean offerFirst(E);
+    method public boolean offerLast(E);
+    method public E peek();
+    method public E peekFirst();
+    method public E peekLast();
+    method public E poll();
+    method public E pollFirst();
+    method public E pollLast();
+    method public E pop();
+    method public void push(E);
+    method public E remove();
+    method public E removeFirst();
+    method public boolean removeFirstOccurrence(java.lang.Object);
+    method public E removeLast();
+    method public boolean removeLastOccurrence(java.lang.Object);
+    method public int size();
+  }
+
   public class ConcurrentLinkedQueue extends java.util.AbstractQueue implements java.util.Queue java.io.Serializable {
     ctor public ConcurrentLinkedQueue();
     ctor public ConcurrentLinkedQueue(java.util.Collection<? extends E>);
@@ -48388,6 +48564,94 @@
     method public static java.util.concurrent.ScheduledExecutorService unconfigurableScheduledExecutorService(java.util.concurrent.ScheduledExecutorService);
   }
 
+  public class ForkJoinPool extends java.util.concurrent.AbstractExecutorService {
+    ctor public ForkJoinPool();
+    ctor public ForkJoinPool(int);
+    ctor public ForkJoinPool(int, java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory, java.lang.Thread.UncaughtExceptionHandler, boolean);
+    method public boolean awaitQuiescence(long, java.util.concurrent.TimeUnit);
+    method public boolean awaitTermination(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method protected int drainTasksTo(java.util.Collection<? super java.util.concurrent.ForkJoinTask<?>>);
+    method public void execute(java.util.concurrent.ForkJoinTask<?>);
+    method public void execute(java.lang.Runnable);
+    method public int getActiveThreadCount();
+    method public boolean getAsyncMode();
+    method public java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory getFactory();
+    method public int getParallelism();
+    method public int getPoolSize();
+    method public int getQueuedSubmissionCount();
+    method public long getQueuedTaskCount();
+    method public int getRunningThreadCount();
+    method public long getStealCount();
+    method public java.lang.Thread.UncaughtExceptionHandler getUncaughtExceptionHandler();
+    method public boolean hasQueuedSubmissions();
+    method public T invoke(java.util.concurrent.ForkJoinTask<T>);
+    method public boolean isQuiescent();
+    method public boolean isShutdown();
+    method public boolean isTerminated();
+    method public boolean isTerminating();
+    method public static void managedBlock(java.util.concurrent.ForkJoinPool.ManagedBlocker) throws java.lang.InterruptedException;
+    method protected java.util.concurrent.ForkJoinTask<?> pollSubmission();
+    method public void shutdown();
+    method public java.util.List<java.lang.Runnable> shutdownNow();
+    method public java.util.concurrent.ForkJoinTask<T> submit(java.util.concurrent.ForkJoinTask<T>);
+    field public static final java.util.concurrent.ForkJoinPool.ForkJoinWorkerThreadFactory defaultForkJoinWorkerThreadFactory;
+  }
+
+  public static abstract interface ForkJoinPool.ForkJoinWorkerThreadFactory {
+    method public abstract java.util.concurrent.ForkJoinWorkerThread newThread(java.util.concurrent.ForkJoinPool);
+  }
+
+  public static abstract interface ForkJoinPool.ManagedBlocker {
+    method public abstract boolean block() throws java.lang.InterruptedException;
+    method public abstract boolean isReleasable();
+  }
+
+  public abstract class ForkJoinTask implements java.util.concurrent.Future java.io.Serializable {
+    ctor public ForkJoinTask();
+    method public static java.util.concurrent.ForkJoinTask<?> adapt(java.lang.Runnable);
+    method public static java.util.concurrent.ForkJoinTask<T> adapt(java.lang.Runnable, T);
+    method public static java.util.concurrent.ForkJoinTask<T> adapt(java.util.concurrent.Callable<? extends T>);
+    method public boolean cancel(boolean);
+    method public void complete(V);
+    method public void completeExceptionally(java.lang.Throwable);
+    method protected abstract boolean exec();
+    method public final java.util.concurrent.ForkJoinTask<V> fork();
+    method public final V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
+    method public final V get(long, java.util.concurrent.TimeUnit) throws java.util.concurrent.ExecutionException, java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    method public final java.lang.Throwable getException();
+    method public static java.util.concurrent.ForkJoinPool getPool();
+    method public static int getQueuedTaskCount();
+    method public abstract V getRawResult();
+    method public static int getSurplusQueuedTaskCount();
+    method public static void helpQuiesce();
+    method public static boolean inForkJoinPool();
+    method public final V invoke();
+    method public static void invokeAll(java.util.concurrent.ForkJoinTask<?>, java.util.concurrent.ForkJoinTask<?>);
+    method public static void invokeAll(java.util.concurrent.ForkJoinTask<?>...);
+    method public static java.util.Collection<T> invokeAll(java.util.Collection<T>);
+    method public final boolean isCancelled();
+    method public final boolean isCompletedAbnormally();
+    method public final boolean isCompletedNormally();
+    method public final boolean isDone();
+    method public final V join();
+    method protected static java.util.concurrent.ForkJoinTask<?> peekNextLocalTask();
+    method protected static java.util.concurrent.ForkJoinTask<?> pollNextLocalTask();
+    method protected static java.util.concurrent.ForkJoinTask<?> pollTask();
+    method public final void quietlyInvoke();
+    method public final void quietlyJoin();
+    method public void reinitialize();
+    method protected abstract void setRawResult(V);
+    method public boolean tryUnfork();
+  }
+
+  public class ForkJoinWorkerThread extends java.lang.Thread {
+    ctor protected ForkJoinWorkerThread(java.util.concurrent.ForkJoinPool);
+    method public java.util.concurrent.ForkJoinPool getPool();
+    method public int getPoolIndex();
+    method protected void onStart();
+    method protected void onTermination(java.lang.Throwable);
+  }
+
   public abstract interface Future {
     method public abstract boolean cancel(boolean);
     method public abstract V get() throws java.util.concurrent.ExecutionException, java.lang.InterruptedException;
@@ -48472,6 +48736,52 @@
     method public E take() throws java.lang.InterruptedException;
   }
 
+  public class LinkedTransferQueue extends java.util.AbstractQueue implements java.io.Serializable java.util.concurrent.TransferQueue {
+    ctor public LinkedTransferQueue();
+    ctor public LinkedTransferQueue(java.util.Collection<? extends E>);
+    method public int drainTo(java.util.Collection<? super E>);
+    method public int drainTo(java.util.Collection<? super E>, int);
+    method public int getWaitingConsumerCount();
+    method public boolean hasWaitingConsumer();
+    method public java.util.Iterator<E> iterator();
+    method public boolean offer(E, long, java.util.concurrent.TimeUnit);
+    method public boolean offer(E);
+    method public E peek();
+    method public E poll(long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+    method public E poll();
+    method public void put(E);
+    method public int remainingCapacity();
+    method public int size();
+    method public E take() throws java.lang.InterruptedException;
+    method public void transfer(E) throws java.lang.InterruptedException;
+    method public boolean tryTransfer(E);
+    method public boolean tryTransfer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+  }
+
+  public class Phaser {
+    ctor public Phaser();
+    ctor public Phaser(int);
+    ctor public Phaser(java.util.concurrent.Phaser);
+    ctor public Phaser(java.util.concurrent.Phaser, int);
+    method public int arrive();
+    method public int arriveAndAwaitAdvance();
+    method public int arriveAndDeregister();
+    method public int awaitAdvance(int);
+    method public int awaitAdvanceInterruptibly(int) throws java.lang.InterruptedException;
+    method public int awaitAdvanceInterruptibly(int, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException, java.util.concurrent.TimeoutException;
+    method public int bulkRegister(int);
+    method public void forceTermination();
+    method public int getArrivedParties();
+    method public java.util.concurrent.Phaser getParent();
+    method public final int getPhase();
+    method public int getRegisteredParties();
+    method public java.util.concurrent.Phaser getRoot();
+    method public int getUnarrivedParties();
+    method public boolean isTerminated();
+    method protected boolean onAdvance(int, int);
+    method public int register();
+  }
+
   public class PriorityBlockingQueue extends java.util.AbstractQueue implements java.util.concurrent.BlockingQueue java.io.Serializable {
     ctor public PriorityBlockingQueue();
     ctor public PriorityBlockingQueue(int);
@@ -48492,6 +48802,22 @@
     method public E take() throws java.lang.InterruptedException;
   }
 
+  public abstract class RecursiveAction extends java.util.concurrent.ForkJoinTask {
+    ctor public RecursiveAction();
+    method protected abstract void compute();
+    method protected final boolean exec();
+    method public final java.lang.Void getRawResult();
+    method protected final void setRawResult(java.lang.Void);
+  }
+
+  public abstract class RecursiveTask extends java.util.concurrent.ForkJoinTask {
+    ctor public RecursiveTask();
+    method protected abstract V compute();
+    method protected final boolean exec();
+    method public final V getRawResult();
+    method protected final void setRawResult(V);
+  }
+
   public class RejectedExecutionException extends java.lang.RuntimeException {
     ctor public RejectedExecutionException();
     ctor public RejectedExecutionException(java.lang.String);
@@ -48530,12 +48856,14 @@
     method protected java.util.concurrent.RunnableScheduledFuture<V> decorateTask(java.util.concurrent.Callable<V>, java.util.concurrent.RunnableScheduledFuture<V>);
     method public boolean getContinueExistingPeriodicTasksAfterShutdownPolicy();
     method public boolean getExecuteExistingDelayedTasksAfterShutdownPolicy();
+    method public boolean getRemoveOnCancelPolicy();
     method public java.util.concurrent.ScheduledFuture<?> schedule(java.lang.Runnable, long, java.util.concurrent.TimeUnit);
     method public java.util.concurrent.ScheduledFuture<V> schedule(java.util.concurrent.Callable<V>, long, java.util.concurrent.TimeUnit);
     method public java.util.concurrent.ScheduledFuture<?> scheduleAtFixedRate(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit);
     method public java.util.concurrent.ScheduledFuture<?> scheduleWithFixedDelay(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit);
     method public void setContinueExistingPeriodicTasksAfterShutdownPolicy(boolean);
     method public void setExecuteExistingDelayedTasksAfterShutdownPolicy(boolean);
+    method public void setRemoveOnCancelPolicy(boolean);
   }
 
   public class Semaphore implements java.io.Serializable {
@@ -48581,6 +48909,15 @@
     method public abstract java.lang.Thread newThread(java.lang.Runnable);
   }
 
+  public class ThreadLocalRandom extends java.util.Random {
+    method public static java.util.concurrent.ThreadLocalRandom current();
+    method public double nextDouble(double);
+    method public double nextDouble(double, double);
+    method public int nextInt(int, int);
+    method public long nextLong(long);
+    method public long nextLong(long, long);
+  }
+
   public class ThreadPoolExecutor extends java.util.concurrent.AbstractExecutorService {
     ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>);
     ctor public ThreadPoolExecutor(int, int, long, java.util.concurrent.TimeUnit, java.util.concurrent.BlockingQueue<java.lang.Runnable>, java.util.concurrent.ThreadFactory);
@@ -48668,6 +49005,14 @@
     ctor public TimeoutException(java.lang.String);
   }
 
+  public abstract interface TransferQueue implements java.util.concurrent.BlockingQueue {
+    method public abstract int getWaitingConsumerCount();
+    method public abstract boolean hasWaitingConsumer();
+    method public abstract void transfer(E) throws java.lang.InterruptedException;
+    method public abstract boolean tryTransfer(E);
+    method public abstract boolean tryTransfer(E, long, java.util.concurrent.TimeUnit) throws java.lang.InterruptedException;
+  }
+
 }
 
 package java.util.concurrent.atomic {
@@ -48877,6 +49222,7 @@
     method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
     method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
     method public final boolean hasContended();
+    method public final boolean hasQueuedPredecessors();
     method public final boolean hasQueuedThreads();
     method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject);
     method protected boolean isHeldExclusively();
@@ -48923,6 +49269,7 @@
     method public final int getWaitQueueLength(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
     method public final java.util.Collection<java.lang.Thread> getWaitingThreads(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
     method public final boolean hasContended();
+    method public final boolean hasQueuedPredecessors();
     method public final boolean hasQueuedThreads();
     method public final boolean hasWaiters(java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject);
     method protected boolean isHeldExclusively();
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index d1bea2e..d683851 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -175,10 +175,13 @@
         System.out.println("Performing full transport backup");
 
         String pkg;
+        ArrayList<String> allPkgs = new ArrayList<String>();
         while ((pkg = nextArg()) != null) {
-            System.out.println("    Package " + pkg + " ...");
+            allPkgs.add(pkg);
+        }
+        if (allPkgs.size() > 0) {
             try {
-                mBmgr.fullTransportBackup(new String[] { pkg });
+                mBmgr.fullTransportBackup(allPkgs.toArray(new String[allPkgs.size()]));
             } catch (RemoteException e) {
                 System.err.println(e.toString());
                 System.err.println(BMGR_NOT_RUNNING_ERR);
@@ -474,6 +477,7 @@
         System.err.println("       bmgr restore PACKAGE");
         System.err.println("       bmgr run");
         System.err.println("       bmgr wipe TRANSPORT PACKAGE");
+        System.err.println("       bmgr fullbackup PACKAGE...");
         System.err.println("");
         System.err.println("The 'backup' command schedules a backup pass for the named package.");
         System.err.println("Note that the backup pass will effectively be a no-op if the package");
@@ -489,11 +493,11 @@
         System.err.println("");
         System.err.println("The 'list transports' command reports the names of the backup transports");
         System.err.println("currently available on the device.  These names can be passed as arguments");
-        System.err.println("to the 'transport' and 'wipe' commands.  The currently selected transport");
+        System.err.println("to the 'transport' and 'wipe' commands.  The currently active transport");
         System.err.println("is indicated with a '*' character.");
         System.err.println("");
         System.err.println("The 'list sets' command reports the token and name of each restore set");
-        System.err.println("available to the device via the current transport.");
+        System.err.println("available to the device via the currently active transport.");
         System.err.println("");
         System.err.println("The 'transport' command designates the named transport as the currently");
         System.err.println("active one.  This setting is persistent across reboots.");
@@ -521,5 +525,8 @@
         System.err.println("erased from the given transport's storage.  The next backup operation");
         System.err.println("that the given application performs will rewrite its entire data set.");
         System.err.println("Transport names to use here are those reported by 'list transports'.");
+        System.err.println("");
+        System.err.println("The 'fullbackup' command induces a full-data stream backup for one or more");
+        System.err.println("packages.  The data is sent via the currently active transport.");
     }
 }
diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp
index ae35f7b..593a197 100644
--- a/cmds/idmap/create.cpp
+++ b/cmds/idmap/create.cpp
@@ -105,7 +105,7 @@
 
         uint32_t cached_target_crc, cached_overlay_crc;
         String8 cached_target_path, cached_overlay_path;
-        if (!ResTable::getIdmapInfo(buf, N, &cached_target_crc, &cached_overlay_crc,
+        if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc,
                     &cached_target_path, &cached_overlay_path)) {
             return true;
         }
diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp
index 46c0edc..90cfa2c 100644
--- a/cmds/idmap/idmap.cpp
+++ b/cmds/idmap/idmap.cpp
@@ -66,26 +66,32 @@
       Display an idmap file: \n\
 \n\
       $ adb shell idmap --inspect /data/resource-cache/vendor@overlay@overlay.apk@idmap \n\
-      SECTION      ENTRY        VALUE      OFFSET    COMMENT \n\
-      IDMAP HEADER magic        0x706d6469 0x0 \n\
-                   base crc     0x484aa77f 0x1 \n\
-                   overlay crc  0x03c66fa5 0x2 \n\
-                   base path    .......... 0x03-0x42 /system/app/target.apk \n\
-                   overlay path .......... 0x43-0x82 /vendor/overlay/overlay.apk \n\
-      DATA HEADER  types count  0x00000003 0x83 \n\
-                   padding      0x00000000 0x84 \n\
-                   type offset  0x00000004 0x85      absolute offset 0x87, xml \n\
-                   type offset  0x00000007 0x86      absolute offset 0x8a, string \n\
-      DATA BLOCK   entry count  0x00000001 0x87 \n\
-                   entry offset 0x00000000 0x88 \n\
-                   entry        0x7f020000 0x89      xml/integer \n\
-      DATA BLOCK   entry count  0x00000002 0x8a \n\
-                   entry offset 0x00000000 0x8b \n\
-                   entry        0x7f030000 0x8c      string/str \n\
-                   entry        0x7f030001 0x8d      string/str2 \n\
+      SECTION      ENTRY        VALUE      COMMENT \n\
+      IDMAP HEADER magic        0x706d6469 \n\
+                   base crc     0xb65a383f \n\
+                   overlay crc  0x7b9675e8 \n\
+                   base path    .......... /path/to/target.apk \n\
+                   overlay path .......... /path/to/overlay.apk \n\
+      DATA HEADER  target pkg   0x0000007f \n\
+                   types count  0x00000003 \n\
+      DATA BLOCK   target type  0x00000002 \n\
+                   overlay type 0x00000002 \n\
+                   entry count  0x00000001 \n\
+                   entry offset 0x00000000 \n\
+                   entry        0x00000000 drawable/drawable \n\
+      DATA BLOCK   target type  0x00000003 \n\
+                   overlay type 0x00000003 \n\
+                   entry count  0x00000001 \n\
+                   entry offset 0x00000000 \n\
+                   entry        0x00000000 xml/integer \n\
+      DATA BLOCK   target type  0x00000004 \n\
+                   overlay type 0x00000004 \n\
+                   entry count  0x00000001 \n\
+                   entry offset 0x00000000 \n\
+                   entry        0x00000000 raw/lorem_ipsum \n\
 \n\
       In this example, the overlay package provides three alternative resource values:\n\
-      xml/integer, string/str and string/str2.\n\
+      drawable/drawable, xml/integer, and raw/lorem_ipsum \n\
 \n\
 NOTES \n\
       This tool and its expected invocation from installd is modelled on dexopt.";
diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp
index a59f5d3..b9ac8a5 100644
--- a/cmds/idmap/inspect.cpp
+++ b/cmds/idmap/inspect.cpp
@@ -10,92 +10,108 @@
 
 using namespace android;
 
-#define NEXT(b, i, o) do { if (buf.next(&i, &o) < 0) { return -1; } } while (0)
-
 namespace {
-    static const uint32_t IDMAP_MAGIC = 0x706d6469;
+    static const uint32_t IDMAP_MAGIC = 0x504D4449;
     static const size_t PATH_LENGTH = 256;
-    static const uint32_t IDMAP_HEADER_SIZE = (3 + 2 * (PATH_LENGTH / sizeof(uint32_t)));
 
     void printe(const char *fmt, ...);
 
     class IdmapBuffer {
         private:
-            char *buf_;
+            const char* buf_;
             size_t len_;
-            mutable size_t pos_;
+            size_t pos_;
         public:
-            IdmapBuffer() : buf_((char *)MAP_FAILED), len_(0), pos_(0) {}
+            IdmapBuffer() : buf_((const char *)MAP_FAILED), len_(0), pos_(0) {}
 
             ~IdmapBuffer() {
                 if (buf_ != MAP_FAILED) {
-                    munmap(buf_, len_);
+                    munmap(const_cast<char*>(buf_), len_);
                 }
             }
 
-            int init(const char *idmap_path)
-            {
+            status_t init(const char *idmap_path) {
                 struct stat st;
                 int fd;
 
                 if (stat(idmap_path, &st) < 0) {
                     printe("failed to stat idmap '%s': %s\n", idmap_path, strerror(errno));
-                    return -1;
+                    return UNKNOWN_ERROR;
                 }
                 len_ = st.st_size;
                 if ((fd = TEMP_FAILURE_RETRY(open(idmap_path, O_RDONLY))) < 0) {
                     printe("failed to open idmap '%s': %s\n", idmap_path, strerror(errno));
-                    return -1;
+                    return UNKNOWN_ERROR;
                 }
-                if ((buf_ = (char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
+                if ((buf_ = (const char*)mmap(NULL, len_, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
                     close(fd);
                     printe("failed to mmap idmap: %s\n", strerror(errno));
-                    return -1;
+                    return UNKNOWN_ERROR;
                 }
                 close(fd);
-                return 0;
+                return NO_ERROR;
             }
 
-            int next(uint32_t *i, uint32_t *offset) const
-            {
+            status_t nextUint32(uint32_t* i) {
                 if (!buf_) {
                     printe("failed to read next uint32_t: buffer not initialized\n");
-                    return -1;
+                    return UNKNOWN_ERROR;
                 }
-                if (pos_ + 4 > len_) {
+
+                if (pos_ + sizeof(uint32_t) > len_) {
                     printe("failed to read next uint32_t: end of buffer reached at pos=0x%08x\n",
                             pos_);
-                    return -1;
+                    return UNKNOWN_ERROR;
                 }
-                *offset = pos_ / sizeof(uint32_t);
-                char a = buf_[pos_++];
-                char b = buf_[pos_++];
-                char c = buf_[pos_++];
-                char d = buf_[pos_++];
-                *i = (d << 24) | (c << 16) | (b << 8) | a;
-                return 0;
+
+                if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x3) != 0) {
+                    printe("failed to read next uint32_t: not aligned on 4-byte boundary\n");
+                    return UNKNOWN_ERROR;
+                }
+
+                *i = dtohl(*reinterpret_cast<const uint32_t*>(buf_ + pos_));
+                pos_ += sizeof(uint32_t);
+                return NO_ERROR;
             }
 
-            int nextPath(char *b, uint32_t *offset_start, uint32_t *offset_end) const
-            {
+            status_t nextUint16(uint16_t* i) {
+                if (!buf_) {
+                    printe("failed to read next uint16_t: buffer not initialized\n");
+                    return UNKNOWN_ERROR;
+                }
+
+                if (pos_ + sizeof(uint16_t) > len_) {
+                    printe("failed to read next uint16_t: end of buffer reached at pos=0x%08x\n",
+                            pos_);
+                    return UNKNOWN_ERROR;
+                }
+
+                if ((reinterpret_cast<uintptr_t>(buf_ + pos_) & 0x1) != 0) {
+                    printe("failed to read next uint32_t: not aligned on 2-byte boundary\n");
+                    return UNKNOWN_ERROR;
+                }
+
+                *i = dtohs(*reinterpret_cast<const uint16_t*>(buf_ + pos_));
+                pos_ += sizeof(uint16_t);
+                return NO_ERROR;
+            }
+
+            status_t nextPath(char *b) {
                 if (!buf_) {
                     printe("failed to read next path: buffer not initialized\n");
-                    return -1;
+                    return UNKNOWN_ERROR;
                 }
                 if (pos_ + PATH_LENGTH > len_) {
                     printe("failed to read next path: end of buffer reached at pos=0x%08x\n", pos_);
-                    return -1;
+                    return UNKNOWN_ERROR;
                 }
                 memcpy(b, buf_ + pos_, PATH_LENGTH);
-                *offset_start = pos_ / sizeof(uint32_t);
                 pos_ += PATH_LENGTH;
-                *offset_end = pos_ / sizeof(uint32_t) - 1;
-                return 0;
+                return NO_ERROR;
             }
     };
 
-    void printe(const char *fmt, ...)
-    {
+    void printe(const char *fmt, ...) {
         va_list ap;
 
         va_start(ap, fmt);
@@ -104,44 +120,37 @@
         va_end(ap);
     }
 
-    void print_header()
-    {
-        printf("SECTION      ENTRY        VALUE      OFFSET    COMMENT\n");
+    void print_header() {
+        printf("SECTION      ENTRY        VALUE      COMMENT\n");
     }
 
-    void print(const char *section, const char *subsection, uint32_t value, uint32_t offset,
-            const char *fmt, ...)
-    {
+    void print(const char *section, const char *subsection, uint32_t value, const char *fmt, ...) {
         va_list ap;
 
         va_start(ap, fmt);
-        printf("%-12s %-12s 0x%08x 0x%-4x    ", section, subsection, value, offset);
+        printf("%-12s %-12s 0x%08x ", section, subsection, value);
         vprintf(fmt, ap);
         printf("\n");
         va_end(ap);
     }
 
-    void print_path(const char *section, const char *subsection, uint32_t offset_start,
-            uint32_t offset_end, const char *fmt, ...)
-    {
+    void print_path(const char *section, const char *subsection, const char *fmt, ...) {
         va_list ap;
 
         va_start(ap, fmt);
-        printf("%-12s %-12s .......... 0x%02x-0x%02x ", section, subsection, offset_start,
-                offset_end);
+        printf("%-12s %-12s .......... ", section, subsection);
         vprintf(fmt, ap);
         printf("\n");
         va_end(ap);
     }
 
-    int resource_metadata(const AssetManager& am, uint32_t res_id,
-            String8 *package, String8 *type, String8 *name)
-    {
+    status_t resource_metadata(const AssetManager& am, uint32_t res_id,
+            String8 *package, String8 *type, String8 *name) {
         const ResTable& rt = am.getResources();
         struct ResTable::resource_name data;
         if (!rt.getResourceName(res_id, false, &data)) {
             printe("failed to get resource name id=0x%08x\n", res_id);
-            return -1;
+            return UNKNOWN_ERROR;
         }
         if (package) {
             *package = String8(String16(data.package, data.packageLen));
@@ -152,140 +161,150 @@
         if (name) {
             *name = String8(String16(data.name, data.nameLen));
         }
-        return 0;
+        return NO_ERROR;
     }
 
-    int package_id(const AssetManager& am)
-    {
-        return (am.getResources().getBasePackageId(0)) << 24;
-    }
-
-    int parse_idmap_header(const IdmapBuffer& buf, AssetManager& am)
-    {
-        uint32_t i, o, e;
+    status_t parse_idmap_header(IdmapBuffer& buf, AssetManager& am) {
+        uint32_t i;
         char path[PATH_LENGTH];
 
-        NEXT(buf, i, o);
+        status_t err = buf.nextUint32(&i);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
         if (i != IDMAP_MAGIC) {
             printe("not an idmap file: actual magic constant 0x%08x does not match expected magic "
                     "constant 0x%08x\n", i, IDMAP_MAGIC);
-            return -1;
+            return UNKNOWN_ERROR;
         }
+
         print_header();
-        print("IDMAP HEADER", "magic", i, o, "");
+        print("IDMAP HEADER", "magic", i, "");
 
-        NEXT(buf, i, o);
-        print("", "base crc", i, o, "");
-
-        NEXT(buf, i, o);
-        print("", "overlay crc", i, o, "");
-
-        if (buf.nextPath(path, &o, &e) < 0) {
-            // printe done from IdmapBuffer::nextPath
-            return -1;
+        err = buf.nextUint32(&i);
+        if (err != NO_ERROR) {
+            return err;
         }
-        print_path("", "base path", o, e, "%s", path);
+        print("", "version", i, "");
+
+        err = buf.nextUint32(&i);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        print("", "base crc", i, "");
+
+        err = buf.nextUint32(&i);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        print("", "overlay crc", i, "");
+
+        err = buf.nextPath(path);
+        if (err != NO_ERROR) {
+            // printe done from IdmapBuffer::nextPath
+            return err;
+        }
+        print_path("", "base path", "%s", path);
+
         if (!am.addAssetPath(String8(path), NULL)) {
             printe("failed to add '%s' as asset path\n", path);
-            return -1;
+            return UNKNOWN_ERROR;
         }
 
-        if (buf.nextPath(path, &o, &e) < 0) {
+        err = buf.nextPath(path);
+        if (err != NO_ERROR) {
             // printe done from IdmapBuffer::nextPath
-            return -1;
+            return err;
         }
-        print_path("", "overlay path", o, e, "%s", path);
+        print_path("", "overlay path", "%s", path);
 
-        return 0;
+        return NO_ERROR;
     }
 
-    int parse_data_header(const IdmapBuffer& buf, const AssetManager& am, Vector<uint32_t>& types)
-    {
-        uint32_t i, o;
-        const uint32_t numeric_package = package_id(am);
+    status_t parse_data(IdmapBuffer& buf, const AssetManager& am) {
+        const uint32_t packageId = am.getResources().getBasePackageId(0);
 
-        NEXT(buf, i, o);
-        print("DATA HEADER", "types count", i, o, "");
-        const uint32_t N = i;
+        uint16_t data16;
+        status_t err = buf.nextUint16(&data16);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        print("DATA HEADER", "target pkg", static_cast<uint32_t>(data16), "");
 
-        for (uint32_t j = 0; j < N; ++j) {
-            NEXT(buf, i, o);
-            if (i == 0) {
-                print("", "padding", i, o, "");
-            } else {
+        err = buf.nextUint16(&data16);
+        if (err != NO_ERROR) {
+            return err;
+        }
+        print("", "types count", static_cast<uint32_t>(data16), "");
+
+        uint32_t typeCount = static_cast<uint32_t>(data16);
+        while (typeCount > 0) {
+            typeCount--;
+
+            err = buf.nextUint16(&data16);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            const uint32_t targetTypeId = static_cast<uint32_t>(data16);
+            print("DATA BLOCK", "target type", targetTypeId, "");
+
+            err = buf.nextUint16(&data16);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            print("", "overlay type", static_cast<uint32_t>(data16), "");
+
+            err = buf.nextUint16(&data16);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            const uint32_t entryCount = static_cast<uint32_t>(data16);
+            print("", "entry count", entryCount, "");
+
+            err = buf.nextUint16(&data16);
+            if (err != NO_ERROR) {
+                return err;
+            }
+            const uint32_t entryOffset = static_cast<uint32_t>(data16);
+            print("", "entry offset", entryOffset, "");
+
+            for (uint32_t i = 0; i < entryCount; i++) {
+                uint32_t data32;
+                err = buf.nextUint32(&data32);
+                if (err != NO_ERROR) {
+                    return err;
+                }
+
+                uint32_t resID = (packageId << 24) | (targetTypeId << 16) | (entryOffset + i);
                 String8 type;
-                const uint32_t numeric_type = (j + 1) << 16;
-                const uint32_t res_id = numeric_package | numeric_type;
-                if (resource_metadata(am, res_id, NULL, &type, NULL) < 0) {
-                    // printe done from resource_metadata
-                    return -1;
+                String8 name;
+                err = resource_metadata(am, resID, NULL, &type, &name);
+                if (err != NO_ERROR) {
+                    return err;
                 }
-                print("", "type offset", i, o, "absolute offset 0x%02x, %s",
-                        i + IDMAP_HEADER_SIZE, type.string());
-                types.add(numeric_type);
+                print("", "entry", data32, "%s/%s", type.string(), name.string());
             }
         }
 
-        return 0;
-    }
-
-    int parse_data_block(const IdmapBuffer& buf, const AssetManager& am, size_t numeric_type)
-    {
-        uint32_t i, o, n, id_offset;
-        const uint32_t numeric_package = package_id(am);
-
-        NEXT(buf, i, o);
-        print("DATA BLOCK", "entry count", i, o, "");
-        n = i;
-
-        NEXT(buf, i, o);
-        print("", "entry offset", i, o, "");
-        id_offset = i;
-
-        for ( ; n > 0; --n) {
-            String8 type, name;
-
-            NEXT(buf, i, o);
-            if (i == 0) {
-                print("", "padding", i, o, "");
-            } else {
-                uint32_t res_id = numeric_package | numeric_type | id_offset;
-                if (resource_metadata(am, res_id, NULL, &type, &name) < 0) {
-                    // printe done from resource_metadata
-                    return -1;
-                }
-                print("", "entry", i, o, "%s/%s", type.string(), name.string());
-            }
-            ++id_offset;
-        }
-
-        return 0;
+        return NO_ERROR;
     }
 }
 
-int idmap_inspect(const char *idmap_path)
-{
+int idmap_inspect(const char *idmap_path) {
     IdmapBuffer buf;
     if (buf.init(idmap_path) < 0) {
         // printe done from IdmapBuffer::init
         return EXIT_FAILURE;
     }
     AssetManager am;
-    if (parse_idmap_header(buf, am) < 0) {
+    if (parse_idmap_header(buf, am) != NO_ERROR) {
         // printe done from parse_idmap_header
         return EXIT_FAILURE;
     }
-    Vector<uint32_t> types;
-    if (parse_data_header(buf, am, types) < 0) {
+    if (parse_data(buf, am) != NO_ERROR) {
         // printe done from parse_data_header
         return EXIT_FAILURE;
     }
-    const size_t N = types.size();
-    for (size_t i = 0; i < N; ++i) {
-        if (parse_data_block(buf, am, types.itemAt(i)) < 0) {
-            // printe done from parse_data_block
-            return EXIT_FAILURE;
-        }
-    }
     return EXIT_SUCCESS;
 }
diff --git a/cmds/media/src/com/android/commands/media/Media.java b/cmds/media/src/com/android/commands/media/Media.java
index 92c6a51..4e91361 100644
--- a/cmds/media/src/com/android/commands/media/Media.java
+++ b/cmds/media/src/com/android/commands/media/Media.java
@@ -17,12 +17,19 @@
 
 package com.android.commands.media;
 
-import android.app.PendingIntent;
+import android.app.ActivityManager;
 import android.content.Context;
-import android.graphics.Bitmap;
-import android.media.IAudioService;
-import android.media.IRemoteControlDisplay;
+import android.media.MediaMetadata;
+import android.media.session.ISessionController;
+import android.media.session.ISessionManager;
+import android.media.session.MediaController;
+import android.media.session.MediaSessionInfo;
+import android.media.session.PlaybackState;
+import android.media.session.RouteInfo;
 import android.os.Bundle;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemClock;
@@ -30,16 +37,17 @@
 import android.view.InputDevice;
 import android.view.KeyCharacterMap;
 import android.view.KeyEvent;
+
 import com.android.internal.os.BaseCommand;
 
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.PrintStream;
+import java.util.List;
 
 public class Media extends BaseCommand {
-
-    private IAudioService mAudioService;
+    private ISessionManager mSessionService;
 
     /**
      * Command-line entry point.
@@ -54,29 +62,35 @@
         out.println(
                 "usage: media [subcommand] [options]\n" +
                 "       media dispatch KEY\n" +
-                "       media remote-display\n" +
+                "       media list-sessions\n" +
+                "       media monitor <sessionId>\n" +
                 "\n" +
-                "media dispatch: dispatch a media key to the current media client.\n" +
+                "media dispatch: dispatch a media key to the system.\n" +
                 "                KEY may be: play, pause, play-pause, mute, headsethook,\n" +
-                "                stop, next, previous, rewind, recordm fast-forword.\n" +
-                "media remote-display: monitor remote display updates.\n"
+                "                stop, next, previous, rewind, record, fast-forword.\n" +
+                "media list-sessions: print a list of the current sessions.\n" +
+                        "media monitor: monitor updates to the specified session.\n" +
+                "                       Use the sessionId from list-sessions.\n"
         );
     }
 
     public void onRun() throws Exception {
-        mAudioService = IAudioService.Stub.asInterface(ServiceManager.checkService(
-                Context.AUDIO_SERVICE));
-        if (mAudioService == null) {
+        mSessionService = ISessionManager.Stub.asInterface(ServiceManager.checkService(
+                Context.MEDIA_SESSION_SERVICE));
+        if (mSessionService == null) {
             System.err.println(NO_SYSTEM_ERROR_CODE);
-            throw new AndroidException("Can't connect to audio service; is the system running?");
+            throw new AndroidException(
+                    "Can't connect to media session service; is the system running?");
         }
 
         String op = nextArgRequired();
 
         if (op.equals("dispatch")) {
             runDispatch();
-        } else if (op.equals("remote-display")) {
-            runRemoteDisplay();
+        } else if (op.equals("list-sessions")) {
+            runListSessions();
+        } else if (op.equals("monitor")) {
+            runMonitor();
         } else {
             showError("Error: unknown command '" + op + "'");
             return;
@@ -85,11 +99,39 @@
 
     private void sendMediaKey(KeyEvent event) {
         try {
-            mAudioService.dispatchMediaKeyEvent(event);
+            mSessionService.dispatchMediaKeyEvent(event, false);
         } catch (RemoteException e) {
         }
     }
 
+    private void runMonitor() throws Exception {
+        String id = nextArgRequired();
+        if (id == null) {
+            showError("Error: must include a session id");
+            return;
+        }
+        boolean success = false;
+        try {
+            List<IBinder> sessions = mSessionService
+                    .getSessions(null, ActivityManager.getCurrentUser());
+            for (IBinder session : sessions) {
+                MediaController controller = MediaController.fromBinder(ISessionController.Stub
+                        .asInterface(session));
+                if (controller != null && controller.getSessionInfo().getId().equals(id)) {
+                    ControllerMonitor monitor = new ControllerMonitor(controller);
+                    monitor.run();
+                    success = true;
+                    break;
+                }
+            }
+        } catch (Exception e) {
+            System.out.println("***Error monitoring session*** " + e.getMessage());
+        }
+        if (!success) {
+            System.out.println("No session found with id " + id);
+        }
+    }
+
     private void runDispatch() throws Exception {
         String cmd = nextArgRequired();
         int keycode;
@@ -127,65 +169,49 @@
                 KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0, InputDevice.SOURCE_KEYBOARD));
     }
 
-    class RemoteDisplayMonitor extends IRemoteControlDisplay.Stub {
-        RemoteDisplayMonitor() {
+    class ControllerMonitor extends MediaController.Callback {
+        private final MediaController mController;
+
+        public ControllerMonitor(MediaController controller) {
+            mController = controller;
         }
-
-
         @Override
-        public void setCurrentClientId(int clientGeneration, PendingIntent clientMediaIntent,
-                boolean clearing) {
-            System.out.println("New client: id=" + clientGeneration
-                    + " intent=" + clientMediaIntent + " clearing=" + clearing);
+        public void onSessionEvent(String event, Bundle extras) {
+            System.out.println("onSessionEvent event=" + event + ", extras=" + extras);
         }
 
         @Override
-        public void setEnabled(boolean enabled) {
-            System.out.println("New enable state= " + (enabled ? "enabled" : "disabled"));
+        public void onRouteChanged(RouteInfo route) {
+            System.out.println("onRouteChanged " + route);
         }
 
         @Override
-        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
-                long currentPosMs, float speed) {
-            System.out.println("New state: id=" + generationId + " state=" + state
-                    + " time=" + stateChangeTimeMs + " pos=" + currentPosMs + " speed=" + speed);
+        public void onPlaybackStateChanged(PlaybackState state) {
+            System.out.println("onPlaybackStateChanged " + state);
         }
 
         @Override
-        public void setTransportControlInfo(int generationId, int transportControlFlags,
-                int posCapabilities) {
-            System.out.println("New control info: id=" + generationId
-                    + " flags=0x" + Integer.toHexString(transportControlFlags)
-                    + " cap=0x" + Integer.toHexString(posCapabilities));
-        }
-
-        @Override
-        public void setMetadata(int generationId, Bundle metadata) {
-            System.out.println("New metadata: id=" + generationId
-                    + " data=" + metadata);
-        }
-
-        @Override
-        public void setArtwork(int generationId, Bitmap artwork) {
-            System.out.println("New artwork: id=" + generationId
-                    + " art=" + artwork);
-        }
-
-        @Override
-        public void setAllMetadata(int generationId, Bundle metadata, Bitmap artwork) {
-            System.out.println("New metadata+artwork: id=" + generationId
-                    + " data=" + metadata + " art=" + artwork);
+        public void onMetadataChanged(MediaMetadata metadata) {
+            String mmString = metadata == null ? null : "title=" + metadata
+                    .getString(MediaMetadata.METADATA_KEY_TITLE);
+            System.out.println("onMetadataChanged " + mmString);
         }
 
         void printUsageMessage() {
-            System.out.println("Monitoring remote control displays...  available commands:");
+            System.out.println("V2Monitoring session " + mController.getSessionInfo().getId()
+                    + "...  available commands:");
             System.out.println("(q)uit: finish monitoring");
         }
 
         void run() throws RemoteException {
             printUsageMessage();
-
-            mAudioService.registerRemoteControlDisplay(this, 0, 0);
+            HandlerThread cbThread = new HandlerThread("MediaCb") {
+                @Override
+                protected void onLooperPrepared() {
+                    mController.addCallback(ControllerMonitor.this);
+                }
+            };
+            cbThread.start();
 
             try {
                 InputStreamReader converter = new InputStreamReader(System.in);
@@ -209,17 +235,35 @@
                         printUsageMessage();
                     }
                 }
-
             } catch (IOException e) {
                 e.printStackTrace();
             } finally {
-                mAudioService.unregisterRemoteControlDisplay(this);
+                cbThread.getLooper().quit();
+                try {
+                    mController.removeCallback(this);
+                } catch (Exception e) {
+                    // ignoring
+                }
             }
         }
     }
 
-    private void runRemoteDisplay() throws Exception {
-        RemoteDisplayMonitor monitor = new RemoteDisplayMonitor();
-        monitor.run();
+    private void runListSessions() {
+        System.out.println("Sessions:");
+        try {
+            List<IBinder> sessions = mSessionService
+                    .getSessions(null, ActivityManager.getCurrentUser());
+            for (IBinder session : sessions) {
+                MediaController controller = MediaController.fromBinder(ISessionController.Stub
+                        .asInterface(session));
+                if (controller != null) {
+                    MediaSessionInfo info = controller.getSessionInfo();
+                    System.out.println("  id=" + info.getId() + ", package="
+                            + info.getPackageName());
+                }
+            }
+        } catch (Exception e) {
+            System.out.println("***Error listing sessions***");
+        }
     }
 }
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 47047b8..f85a7dc 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -58,11 +58,13 @@
 import java.util.Comparator;
 import java.util.List;
 import java.util.WeakHashMap;
+
 import javax.crypto.SecretKey;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
 import com.android.internal.content.PackageHelper;
+import com.android.internal.util.ArrayUtils;
 
 public final class Pm {
     IPackageManager mPm;
@@ -1548,6 +1550,12 @@
             if (info != null && info.applicationInfo != null) {
                 System.out.print("package:");
                 System.out.println(info.applicationInfo.sourceDir);
+                if (!ArrayUtils.isEmpty(info.applicationInfo.splitSourceDirs)) {
+                    for (String splitSourceDir : info.applicationInfo.splitSourceDirs) {
+                        System.out.print("package:");
+                        System.out.println(splitSourceDir);
+                    }
+                }
             }
         } catch (RemoteException e) {
             System.err.println(e.toString());
diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp
index 6296dd1..6b2a0e2 100644
--- a/cmds/screencap/screencap.cpp
+++ b/cmds/screencap/screencap.cpp
@@ -52,13 +52,13 @@
     );
 }
 
-static SkBitmap::Config flinger2skia(PixelFormat f)
+static SkColorType flinger2skia(PixelFormat f)
 {
     switch (f) {
         case PIXEL_FORMAT_RGB_565:
-            return SkBitmap::kRGB_565_Config;
+            return kRGB_565_SkColorType;
         default:
-            return SkBitmap::kARGB_8888_Config;
+            return kN32_SkColorType;
     }
 }
 
@@ -174,9 +174,10 @@
 
     if (base) {
         if (png) {
+            const SkImageInfo info = SkImageInfo::Make(w, h, flinger2skia(f),
+                                                       kPremul_SkAlphaType);
             SkBitmap b;
-            b.setConfig(flinger2skia(f), w, h, s*bytesPerPixel(f));
-            b.setPixels((void*)base);
+            b.installPixels(info, const_cast<void*>(base), s*bytesPerPixel(f));
             SkDynamicMemoryWStream stream;
             SkImageEncoder::EncodeStream(&stream, b,
                     SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality);
diff --git a/core/java/android/animation/AnimatorInflater.java b/core/java/android/animation/AnimatorInflater.java
index 06f5aca..0f5e954 100644
--- a/core/java/android/animation/AnimatorInflater.java
+++ b/core/java/android/animation/AnimatorInflater.java
@@ -17,14 +17,18 @@
 
 import android.content.Context;
 import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
 import android.content.res.Resources.Theme;
 import android.content.res.TypedArray;
 import android.content.res.XmlResourceParser;
-import android.content.res.Resources.NotFoundException;
+import android.graphics.Path;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.util.PathParser;
 import android.util.StateSet;
 import android.util.TypedValue;
 import android.util.Xml;
+import android.view.InflateException;
 import android.view.animation.AnimationUtils;
 
 import com.android.internal.R;
@@ -45,7 +49,7 @@
  * <em>something</em> file.)
  */
 public class AnimatorInflater {
-
+    private static final String TAG = "AnimatorInflater";
     /**
      * These flags are used when parsing AnimatorSet objects
      */
@@ -57,9 +61,12 @@
      */
     private static final int VALUE_TYPE_FLOAT       = 0;
     private static final int VALUE_TYPE_INT         = 1;
+    private static final int VALUE_TYPE_PATH        = 2;
     private static final int VALUE_TYPE_COLOR       = 4;
     private static final int VALUE_TYPE_CUSTOM      = 5;
 
+    private static final boolean DBG_ANIMATOR_INFLATER = false;
+
     /**
      * Loads an {@link Animator} object from a resource
      *
@@ -158,7 +165,7 @@
                         int stateIndex = 0;
                         for (int i = 0; i < attributeCount; i++) {
                             int attrName = attributeSet.getAttributeNameResource(i);
-                            if (attrName == com.android.internal.R.attr.animation) {
+                            if (attrName == R.attr.animation) {
                                 animator = loadAnimator(context,
                                         attributeSet.getAttributeResourceValue(i, 0));
                             } else {
@@ -186,55 +193,242 @@
         }
     }
 
+    /**
+     * PathDataEvaluator is used to interpolate between two paths which are
+     * represented in the same format but different control points' values.
+     * The path is represented as an array of PathDataNode here, which is
+     * fundamentally an array of floating point numbers.
+     */
+    private static class PathDataEvaluator implements TypeEvaluator<PathParser.PathDataNode[]> {
+        private PathParser.PathDataNode[] mNodeArray;
 
-    private static void parseAnimatorFromTypeArray(ValueAnimator anim, TypedArray a) {
-        long duration = a.getInt(com.android.internal.R.styleable.Animator_duration, 300);
+        /**
+         * Create a PathParser.PathDataNode[] that does not reuse the animated value.
+         * Care must be taken when using this option because on every evaluation
+         * a new <code>PathParser.PathDataNode[]</code> will be allocated.
+         */
+        private PathDataEvaluator() {}
 
-        long startDelay = a.getInt(com.android.internal.R.styleable.Animator_startOffset, 0);
+        /**
+         * Create a PathDataEvaluator that reuses <code>nodeArray</code> for every evaluate() call.
+         * Caution must be taken to ensure that the value returned from
+         * {@link android.animation.ValueAnimator#getAnimatedValue()} is not cached, modified, or
+         * used across threads. The value will be modified on each <code>evaluate()</code> call.
+         *
+         * @param nodeArray The array to modify and return from <code>evaluate</code>.
+         */
+        public PathDataEvaluator(PathParser.PathDataNode[] nodeArray) {
+            mNodeArray = nodeArray;
+        }
 
-        int valueType = a.getInt(com.android.internal.R.styleable.Animator_valueType,
+        @Override
+        public PathParser.PathDataNode[] evaluate(float fraction,
+                PathParser.PathDataNode[] startPathData,
+                PathParser.PathDataNode[] endPathData) {
+            if (!PathParser.canMorph(startPathData, endPathData)) {
+                throw new IllegalArgumentException("Can't interpolate between"
+                        + " two incompatible pathData");
+            }
+
+            if (mNodeArray == null || !PathParser.canMorph(mNodeArray, startPathData)) {
+                mNodeArray = PathParser.deepCopyNodes(startPathData);
+            }
+
+            for (int i = 0; i < startPathData.length; i++) {
+                mNodeArray[i].interpolatePathDataNode(startPathData[i],
+                        endPathData[i], fraction);
+            }
+
+            return mNodeArray;
+        }
+    }
+
+    /**
+     * @param anim Null if this is a ValueAnimator, otherwise this is an
+     *            ObjectAnimator
+     * @param arrayAnimator Incoming typed array for Animator's attributes.
+     * @param arrayObjectAnimator Incoming typed array for Object Animator's
+     *            attributes.
+     */
+    private static void parseAnimatorFromTypeArray(ValueAnimator anim,
+            TypedArray arrayAnimator, TypedArray arrayObjectAnimator) {
+        long duration = arrayAnimator.getInt(R.styleable.Animator_duration, 300);
+
+        long startDelay = arrayAnimator.getInt(R.styleable.Animator_startOffset, 0);
+
+        int valueType = arrayAnimator.getInt(R.styleable.Animator_valueType,
                 VALUE_TYPE_FLOAT);
 
         if (anim == null) {
             anim = new ValueAnimator();
         }
-        TypeEvaluator evaluator = null;
 
-        int valueFromIndex = com.android.internal.R.styleable.Animator_valueFrom;
-        int valueToIndex = com.android.internal.R.styleable.Animator_valueTo;
+        TypeEvaluator evaluator = null;
 
         boolean getFloats = (valueType == VALUE_TYPE_FLOAT);
 
-        TypedValue tvFrom = a.peekValue(valueFromIndex);
+        TypedValue tvFrom = arrayAnimator.peekValue(R.styleable.Animator_valueFrom);
         boolean hasFrom = (tvFrom != null);
         int fromType = hasFrom ? tvFrom.type : 0;
-        TypedValue tvTo = a.peekValue(valueToIndex);
+        TypedValue tvTo = arrayAnimator.peekValue(R.styleable.Animator_valueTo);
         boolean hasTo = (tvTo != null);
         int toType = hasTo ? tvTo.type : 0;
 
-        if ((hasFrom && (fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
-                (fromType <= TypedValue.TYPE_LAST_COLOR_INT)) ||
-            (hasTo && (toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
-                (toType <= TypedValue.TYPE_LAST_COLOR_INT))) {
-            // special case for colors: ignore valueType and get ints
-            getFloats = false;
-            evaluator = ArgbEvaluator.getInstance();
+        // TODO: Further clean up this part of code into 4 types : path, color,
+        // integer and float.
+        if (valueType == VALUE_TYPE_PATH) {
+            evaluator = setupAnimatorForPath(anim, arrayAnimator);
+        } else {
+            // Integer and float value types are handled here.
+            if ((hasFrom && (fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
+                    (fromType <= TypedValue.TYPE_LAST_COLOR_INT)) ||
+                    (hasTo && (toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
+                            (toType <= TypedValue.TYPE_LAST_COLOR_INT))) {
+                // special case for colors: ignore valueType and get ints
+                getFloats = false;
+                evaluator = ArgbEvaluator.getInstance();
+            }
+            setupValues(anim, arrayAnimator, getFloats, hasFrom, fromType, hasTo, toType);
         }
 
+        anim.setDuration(duration);
+        anim.setStartDelay(startDelay);
+
+        if (arrayAnimator.hasValue(R.styleable.Animator_repeatCount)) {
+            anim.setRepeatCount(
+                    arrayAnimator.getInt(R.styleable.Animator_repeatCount, 0));
+        }
+        if (arrayAnimator.hasValue(R.styleable.Animator_repeatMode)) {
+            anim.setRepeatMode(
+                    arrayAnimator.getInt(R.styleable.Animator_repeatMode,
+                            ValueAnimator.RESTART));
+        }
+        if (evaluator != null) {
+            anim.setEvaluator(evaluator);
+        }
+
+        if (arrayObjectAnimator != null) {
+            setupObjectAnimator(anim, arrayObjectAnimator, getFloats);
+        }
+    }
+
+    /**
+     * Setup the Animator to achieve path morphing.
+     *
+     * @param anim The target Animator which will be updated.
+     * @param arrayAnimator TypedArray for the ValueAnimator.
+     * @return the PathDataEvaluator.
+     */
+    private static TypeEvaluator setupAnimatorForPath(ValueAnimator anim,
+             TypedArray arrayAnimator) {
+        TypeEvaluator evaluator = null;
+        String fromString = arrayAnimator.getString(R.styleable.Animator_valueFrom);
+        String toString = arrayAnimator.getString(R.styleable.Animator_valueTo);
+        PathParser.PathDataNode[] nodesFrom = PathParser.createNodesFromPathData(fromString);
+        PathParser.PathDataNode[] nodesTo = PathParser.createNodesFromPathData(toString);
+
+        if (nodesFrom != null) {
+            if (nodesTo != null) {
+                anim.setObjectValues(nodesFrom, nodesTo);
+                if (!PathParser.canMorph(nodesFrom, nodesTo)) {
+                    throw new InflateException(arrayAnimator.getPositionDescription()
+                            + " Can't morph from " + fromString + " to " + toString);
+                }
+            } else {
+                anim.setObjectValues((Object)nodesFrom);
+            }
+            evaluator = new PathDataEvaluator(PathParser.deepCopyNodes(nodesFrom));
+        } else if (nodesTo != null) {
+            anim.setObjectValues((Object)nodesTo);
+            evaluator = new PathDataEvaluator(PathParser.deepCopyNodes(nodesTo));
+        }
+
+        if (DBG_ANIMATOR_INFLATER && evaluator != null) {
+            Log.v(TAG, "create a new PathDataEvaluator here");
+        }
+
+        return evaluator;
+    }
+
+    /**
+     * Setup ObjectAnimator's property or values from pathData.
+     *
+     * @param anim The target Animator which will be updated.
+     * @param arrayObjectAnimator TypedArray for the ObjectAnimator.
+     * @param getFloats True if the value type is float.
+     */
+    private static void setupObjectAnimator(ValueAnimator anim, TypedArray arrayObjectAnimator,
+            boolean getFloats) {
+        ObjectAnimator oa = (ObjectAnimator) anim;
+        String pathData = arrayObjectAnimator.getString(R.styleable.PropertyAnimator_pathData);
+
+        // Note that if there is a pathData defined in the Object Animator,
+        // valueFrom / valueTo will be ignored.
+        if (pathData != null) {
+            String propertyXName =
+                    arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyXName);
+            String propertyYName =
+                    arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyYName);
+
+            if (propertyXName == null && propertyYName == null) {
+                throw new InflateException(arrayObjectAnimator.getPositionDescription()
+                        + " propertyXName or propertyYName is needed for PathData");
+            } else {
+                Path path = PathParser.createPathFromPathData(pathData);
+                Keyframe[][] keyframes = PropertyValuesHolder.createKeyframes(path, !getFloats);
+                PropertyValuesHolder x = null;
+                PropertyValuesHolder y = null;
+                if (propertyXName != null) {
+                    x = PropertyValuesHolder.ofKeyframe(propertyXName, keyframes[0]);
+                }
+                if (propertyYName != null) {
+                    y = PropertyValuesHolder.ofKeyframe(propertyYName, keyframes[1]);
+                }
+                if (x == null) {
+                    oa.setValues(y);
+                } else if (y == null) {
+                    oa.setValues(x);
+                } else {
+                    oa.setValues(x, y);
+                }
+            }
+        } else {
+            String propertyName =
+                    arrayObjectAnimator.getString(R.styleable.PropertyAnimator_propertyName);
+            oa.setPropertyName(propertyName);
+        }
+    }
+
+    /**
+     * Setup ValueAnimator's values.
+     * This will handle all of the integer, float and color types.
+     *
+     * @param anim The target Animator which will be updated.
+     * @param arrayAnimator TypedArray for the ValueAnimator.
+     * @param getFloats True if the value type is float.
+     * @param hasFrom True if "valueFrom" exists.
+     * @param fromType The type of "valueFrom".
+     * @param hasTo True if "valueTo" exists.
+     * @param toType The type of "valueTo".
+     */
+    private static void setupValues(ValueAnimator anim, TypedArray arrayAnimator,
+            boolean getFloats, boolean hasFrom, int fromType, boolean hasTo, int toType) {
+        int valueFromIndex = R.styleable.Animator_valueFrom;
+        int valueToIndex = R.styleable.Animator_valueTo;
         if (getFloats) {
             float valueFrom;
             float valueTo;
             if (hasFrom) {
                 if (fromType == TypedValue.TYPE_DIMENSION) {
-                    valueFrom = a.getDimension(valueFromIndex, 0f);
+                    valueFrom = arrayAnimator.getDimension(valueFromIndex, 0f);
                 } else {
-                    valueFrom = a.getFloat(valueFromIndex, 0f);
+                    valueFrom = arrayAnimator.getFloat(valueFromIndex, 0f);
                 }
                 if (hasTo) {
                     if (toType == TypedValue.TYPE_DIMENSION) {
-                        valueTo = a.getDimension(valueToIndex, 0f);
+                        valueTo = arrayAnimator.getDimension(valueToIndex, 0f);
                     } else {
-                        valueTo = a.getFloat(valueToIndex, 0f);
+                        valueTo = arrayAnimator.getFloat(valueToIndex, 0f);
                     }
                     anim.setFloatValues(valueFrom, valueTo);
                 } else {
@@ -242,9 +436,9 @@
                 }
             } else {
                 if (toType == TypedValue.TYPE_DIMENSION) {
-                    valueTo = a.getDimension(valueToIndex, 0f);
+                    valueTo = arrayAnimator.getDimension(valueToIndex, 0f);
                 } else {
-                    valueTo = a.getFloat(valueToIndex, 0f);
+                    valueTo = arrayAnimator.getFloat(valueToIndex, 0f);
                 }
                 anim.setFloatValues(valueTo);
             }
@@ -253,21 +447,21 @@
             int valueTo;
             if (hasFrom) {
                 if (fromType == TypedValue.TYPE_DIMENSION) {
-                    valueFrom = (int) a.getDimension(valueFromIndex, 0f);
+                    valueFrom = (int) arrayAnimator.getDimension(valueFromIndex, 0f);
                 } else if ((fromType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
                         (fromType <= TypedValue.TYPE_LAST_COLOR_INT)) {
-                    valueFrom = a.getColor(valueFromIndex, 0);
+                    valueFrom = arrayAnimator.getColor(valueFromIndex, 0);
                 } else {
-                    valueFrom = a.getInt(valueFromIndex, 0);
+                    valueFrom = arrayAnimator.getInt(valueFromIndex, 0);
                 }
                 if (hasTo) {
                     if (toType == TypedValue.TYPE_DIMENSION) {
-                        valueTo = (int) a.getDimension(valueToIndex, 0f);
+                        valueTo = (int) arrayAnimator.getDimension(valueToIndex, 0f);
                     } else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
                             (toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
-                        valueTo = a.getColor(valueToIndex, 0);
+                        valueTo = arrayAnimator.getColor(valueToIndex, 0);
                     } else {
-                        valueTo = a.getInt(valueToIndex, 0);
+                        valueTo = arrayAnimator.getInt(valueToIndex, 0);
                     }
                     anim.setIntValues(valueFrom, valueTo);
                 } else {
@@ -276,33 +470,17 @@
             } else {
                 if (hasTo) {
                     if (toType == TypedValue.TYPE_DIMENSION) {
-                        valueTo = (int) a.getDimension(valueToIndex, 0f);
+                        valueTo = (int) arrayAnimator.getDimension(valueToIndex, 0f);
                     } else if ((toType >= TypedValue.TYPE_FIRST_COLOR_INT) &&
-                        (toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
-                        valueTo = a.getColor(valueToIndex, 0);
+                            (toType <= TypedValue.TYPE_LAST_COLOR_INT)) {
+                        valueTo = arrayAnimator.getColor(valueToIndex, 0);
                     } else {
-                        valueTo = a.getInt(valueToIndex, 0);
+                        valueTo = arrayAnimator.getInt(valueToIndex, 0);
                     }
                     anim.setIntValues(valueTo);
                 }
             }
         }
-
-        anim.setDuration(duration);
-        anim.setStartDelay(startDelay);
-
-        if (a.hasValue(com.android.internal.R.styleable.Animator_repeatCount)) {
-            anim.setRepeatCount(
-                    a.getInt(com.android.internal.R.styleable.Animator_repeatCount, 0));
-        }
-        if (a.hasValue(com.android.internal.R.styleable.Animator_repeatMode)) {
-            anim.setRepeatMode(
-                    a.getInt(com.android.internal.R.styleable.Animator_repeatMode,
-                            ValueAnimator.RESTART));
-        }
-        if (evaluator != null) {
-            anim.setEvaluator(evaluator);
-        }
     }
 
     private static Animator createAnimatorFromXml(Resources res, Theme theme, XmlPullParser parser)
@@ -338,11 +516,11 @@
                 anim = new AnimatorSet();
                 TypedArray a;
                 if (theme != null) {
-                    a = theme.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AnimatorSet, 0, 0);
+                    a = theme.obtainStyledAttributes(attrs, R.styleable.AnimatorSet, 0, 0);
                 } else {
-                    a = res.obtainAttributes(attrs, com.android.internal.R.styleable.AnimatorSet);
+                    a = res.obtainAttributes(attrs, R.styleable.AnimatorSet);
                 }
-                int ordering = a.getInt(com.android.internal.R.styleable.AnimatorSet_ordering,
+                int ordering = a.getInt(R.styleable.AnimatorSet_ordering,
                         TOGETHER);
                 createAnimatorFromXml(res, theme, parser, attrs, (AnimatorSet) anim, ordering);
                 a.recycle();
@@ -380,19 +558,6 @@
 
         loadAnimator(res, theme, attrs, anim);
 
-        TypedArray a;
-        if (theme != null) {
-            a = theme.obtainStyledAttributes(attrs, R.styleable.PropertyAnimator, 0, 0);
-        } else {
-            a = res.obtainAttributes(attrs, R.styleable.PropertyAnimator);
-        }
-
-        String propertyName = a.getString(R.styleable.PropertyAnimator_propertyName);
-
-        anim.setPropertyName(propertyName);
-
-        a.recycle();
-
         return anim;
     }
 
@@ -402,26 +567,41 @@
      *
      * @param res The resources
      * @param attrs The set of attributes holding the animation parameters
+     * @param anim Null if this is a ValueAnimator, otherwise this is an
+     *            ObjectAnimator
      */
     private static ValueAnimator loadAnimator(Resources res, Theme theme,
             AttributeSet attrs, ValueAnimator anim)
             throws NotFoundException {
 
-        TypedArray a;
+        TypedArray arrayAnimator = null;
+        TypedArray arrayObjectAnimator = null;
+
         if (theme != null) {
-            a = theme.obtainStyledAttributes(attrs, R.styleable.Animator, 0, 0);
+            arrayAnimator = theme.obtainStyledAttributes(attrs, R.styleable.Animator, 0, 0);
         } else {
-            a = res.obtainAttributes(attrs, R.styleable.Animator);
+            arrayAnimator = res.obtainAttributes(attrs, R.styleable.Animator);
         }
 
-        parseAnimatorFromTypeArray(anim, a);
+        // If anim is not null, then it is an object animator.
+        if (anim != null) {
+            if (theme != null) {
+                arrayObjectAnimator = theme.obtainStyledAttributes(attrs,
+                        R.styleable.PropertyAnimator, 0, 0);
+            } else {
+                arrayObjectAnimator = res.obtainAttributes(attrs, R.styleable.PropertyAnimator);
+            }
+        }
+        parseAnimatorFromTypeArray(anim, arrayAnimator, arrayObjectAnimator);
 
         final int resID =
-                a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
+                arrayAnimator.getResourceId(R.styleable.Animator_interpolator, 0);
         if (resID > 0) {
             anim.setInterpolator(AnimationUtils.loadInterpolator(res, theme, resID));
         }
-        a.recycle();
+
+        arrayAnimator.recycle();
+        arrayObjectAnimator.recycle();
 
         return anim;
     }
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index 8947550..da56a77 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -469,7 +469,10 @@
      */
     public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> xProperty,
             Property<T, Float> yProperty, Path path) {
-        return ofFloat(target, xProperty.getName(), yProperty.getName(), path);
+        Keyframe[][] keyframes = PropertyValuesHolder.createKeyframes(path, false);
+        PropertyValuesHolder x = PropertyValuesHolder.ofKeyframe(xProperty, keyframes[0]);
+        PropertyValuesHolder y = PropertyValuesHolder.ofKeyframe(yProperty, keyframes[1]);
+        return ofPropertyValuesHolder(target, x, y);
     }
 
     /**
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index f6883e2..83cbaa1 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -30,6 +30,7 @@
 
 import android.annotation.IntDef;
 import android.annotation.Nullable;
+import android.app.admin.DevicePolicyManager;
 import android.content.ComponentCallbacks2;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -929,7 +930,8 @@
 
     /**
      * Same as {@link #onCreate(android.os.Bundle)} but called for those activities created with
-     * the attribute {@link android.R.attr#persistable} set true.
+     * the attribute {@link android.R.attr#persistableMode} set to
+     * <code>persistAcrossReboots</code>.
      *
      * @param savedInstanceState if the activity is being re-initialized after
      *     previously being shut down then this Bundle contains the data it most
@@ -1012,8 +1014,9 @@
 
     /**
      * This is the same as {@link #onRestoreInstanceState(Bundle)} but is called for activities
-     * created with the attribute {@link android.R.attr#persistable}. The {@link
-     * android.os.PersistableBundle} passed came from the restored PersistableBundle first
+     * created with the attribute {@link android.R.attr#persistableMode} set to
+     * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed
+     * came from the restored PersistableBundle first
      * saved in {@link #onSaveInstanceState(Bundle, PersistableBundle)}.
      *
      * <p>This method is called between {@link #onStart} and
@@ -1111,7 +1114,8 @@
 
     /**
      * This is the same as {@link #onPostCreate(Bundle)} but is called for activities
-     * created with the attribute {@link android.R.attr#persistable}.
+     * created with the attribute {@link android.R.attr#persistableMode} set to
+     * <code>persistAcrossReboots</code>.
      *
      * @param savedInstanceState The data most recently supplied in {@link #onSaveInstanceState}
      * @param persistentState The data caming from the PersistableBundle first
@@ -1352,10 +1356,10 @@
 
     /**
      * This is the same as {@link #onSaveInstanceState} but is called for activities
-     * created with the attribute {@link android.R.attr#persistable}. The {@link
-     * android.os.PersistableBundle} passed in will be saved and presented in
-     * {@link #onCreate(Bundle, PersistableBundle)} the first time that this activity
-     * is restarted following the next device reboot.
+     * created with the attribute {@link android.R.attr#persistableMode} set to
+     * <code>persistAcrossReboots</code>. The {@link android.os.PersistableBundle} passed
+     * in will be saved and presented in {@link #onCreate(Bundle, PersistableBundle)}
+     * the first time that this activity is restarted following the next device reboot.
      *
      * @param outState Bundle in which to place your saved state.
      * @param outPersistentState State which will be saved across reboots.
@@ -5135,78 +5139,7 @@
             return onCreateView(name, context, attrs);
         }
         
-        String fname = attrs.getAttributeValue(null, "class");
-        TypedArray a = 
-            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);
-        if (fname == null) {
-            fname = a.getString(com.android.internal.R.styleable.Fragment_name);
-        }
-        int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, View.NO_ID);
-        String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);
-        a.recycle();
-        
-        int containerId = parent != null ? parent.getId() : 0;
-        if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
-            throw new IllegalArgumentException(attrs.getPositionDescription()
-                    + ": Must specify unique android:id, android:tag, or have a parent with an id for " + fname);
-        }
-
-        // If we restored from a previous state, we may already have
-        // instantiated this fragment from the state and should use
-        // that instance instead of making a new one.
-        Fragment fragment = id != View.NO_ID ? mFragments.findFragmentById(id) : null;
-        if (fragment == null && tag != null) {
-            fragment = mFragments.findFragmentByTag(tag);
-        }
-        if (fragment == null && containerId != View.NO_ID) {
-            fragment = mFragments.findFragmentById(containerId);
-        }
-
-        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
-                + Integer.toHexString(id) + " fname=" + fname
-                + " existing=" + fragment);
-        if (fragment == null) {
-            fragment = Fragment.instantiate(this, fname);
-            fragment.mFromLayout = true;
-            fragment.mFragmentId = id != 0 ? id : containerId;
-            fragment.mContainerId = containerId;
-            fragment.mTag = tag;
-            fragment.mInLayout = true;
-            fragment.mFragmentManager = mFragments;
-            fragment.onInflate(this, attrs, fragment.mSavedFragmentState);
-            mFragments.addFragment(fragment, true);
-
-        } else if (fragment.mInLayout) {
-            // A fragment already exists and it is not one we restored from
-            // previous state.
-            throw new IllegalArgumentException(attrs.getPositionDescription()
-                    + ": Duplicate id 0x" + Integer.toHexString(id)
-                    + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)
-                    + " with another fragment for " + fname);
-        } else {
-            // This fragment was retained from a previous instance; get it
-            // going now.
-            fragment.mInLayout = true;
-            // If this fragment is newly instantiated (either right now, or
-            // from last saved state), then give it the attributes to
-            // initialize itself.
-            if (!fragment.mRetaining) {
-                fragment.onInflate(this, attrs, fragment.mSavedFragmentState);
-            }
-            mFragments.moveToState(fragment);
-        }
-
-        if (fragment.mView == null) {
-            throw new IllegalStateException("Fragment " + fname
-                    + " did not create a view.");
-        }
-        if (id != 0) {
-            fragment.mView.setId(id);
-        }
-        if (fragment.mView.getTag() == null) {
-            fragment.mView.setTag(tag);
-        }
-        return fragment.mView;
+        return mFragments.onCreateView(parent, name, context, attrs);
     }
 
     /**
@@ -5320,13 +5253,15 @@
      * drawn and it is safe to make this Activity translucent again.
      * @param options activity options delivered to the activity below this one. The options
      * are retrieved using {@link #getActivityOptions}.
+     * @return <code>true</code> if Window was opaque and will become translucent or
+     * <code>false</code> if window was translucent and no change needed to be made.
      *
      * @see #convertFromTranslucent()
      * @see TranslucentConversionListener
      *
      * @hide
      */
-    public void convertToTranslucent(TranslucentConversionListener callback, 
+    public boolean convertToTranslucent(TranslucentConversionListener callback,
             ActivityOptions options) {
         boolean drawComplete;
         try {
@@ -5343,6 +5278,7 @@
             // Window is already translucent.
             mTranslucentCallback.onTranslucentConversionComplete(drawComplete);
         }
+        return mChangeCanvasToTranslucent;
     }
 
     /** @hide */
@@ -5929,14 +5865,21 @@
     }
 
     /**
-     * Put this Activity in a mode where the user is locked to the
+     * Request to put this Activity in a mode where the user is locked to the
      * current task.
      *
      * This will prevent the user from launching other apps, going to settings,
      * or reaching the home screen.
      *
-     * Lock task mode will only start if the activity has been whitelisted by the
-     * Device Owner through DevicePolicyManager#setLockTaskComponents.
+     * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns true
+     * for this component then the app will go directly into Lock Task mode.  The user
+     * will not be able to exit this mode until {@link Activity#stopLockTask()} is called.
+     *
+     * If {@link DevicePolicyManager#isLockTaskPermitted(String)} returns false
+     * then the system will prompt the user with a dialog requesting permission to enter
+     * this mode.  When entered through this method the user can exit at any time by
+     * swiping down twice from the top of the screen.  Calling stopLockTask will also
+     * exit the mode.
      */
     public void startLockTask() {
         try {
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 56462ae..572d389 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2145,6 +2145,13 @@
             return true;
         }
 
+        case START_LOCK_TASK_BY_CURRENT: {
+            data.enforceInterface(IActivityManager.descriptor);
+            startLockTaskModeOnCurrent();
+            reply.writeNoException();
+            return true;
+        }
+
         case STOP_LOCK_TASK_MODE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             stopLockTaskMode();
@@ -2152,6 +2159,13 @@
             return true;
         }
 
+        case STOP_LOCK_TASK_BY_CURRENT: {
+            data.enforceInterface(IActivityManager.descriptor);
+            stopLockTaskModeOnCurrent();
+            reply.writeNoException();
+            return true;
+        }
+
         case IS_IN_LOCK_TASK_MODE_TRANSACTION: {
             data.enforceInterface(IActivityManager.descriptor);
             final boolean isInLockTaskMode = isInLockTaskMode();
@@ -4947,6 +4961,17 @@
     }
 
     @Override
+    public void startLockTaskModeOnCurrent() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(START_LOCK_TASK_BY_CURRENT, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    @Override
     public void stopLockTaskMode() throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
@@ -4958,6 +4983,17 @@
     }
 
     @Override
+    public void stopLockTaskModeOnCurrent() throws RemoteException {
+        Parcel data = Parcel.obtain();
+        Parcel reply = Parcel.obtain();
+        data.writeInterfaceToken(IActivityManager.descriptor);
+        mRemote.transact(STOP_LOCK_TASK_BY_CURRENT, data, reply, 0);
+        reply.readException();
+        data.recycle();
+        reply.recycle();
+    }
+
+    @Override
     public boolean isInLockTaskMode() throws RemoteException {
         Parcel data = Parcel.obtain();
         Parcel reply = Parcel.obtain();
diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java
index 9160452..0ecead9 100644
--- a/core/java/android/app/ActivityOptions.java
+++ b/core/java/android/app/ActivityOptions.java
@@ -29,8 +29,6 @@
 import android.view.Window;
 
 import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
 
 /**
  * Helper class for building an options Bundle that can be used with
@@ -398,14 +396,14 @@
                 if (sharedElementName == null) {
                     throw new IllegalArgumentException("Shared element name must not be null");
                 }
-                String viewName = sharedElement.first.getViewName();
-                if (viewName == null) {
+                String name = sharedElement.first.getTransitionName();
+                if (name == null) {
                     throw new IllegalArgumentException("Shared elements must have non-null " +
-                            "viewNames");
+                            "transitionNames");
                 }
 
                 names.add(sharedElementName);
-                mappedNames.add(viewName);
+                mappedNames.add(name);
             }
         }
 
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index ea46044..f5514f8 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -191,11 +191,13 @@
     /** Reference to singleton {@link ActivityThread} */
     private static ActivityThread sCurrentActivityThread;
     Instrumentation mInstrumentation;
+    String mInstrumentationPackageName = null;
     String mInstrumentationAppDir = null;
-    String mInstrumentationAppLibraryDir = null;
-    String mInstrumentationAppPackage = null;
+    String[] mInstrumentationSplitAppDirs = null;
+    String mInstrumentationLibDir = null;
     String mInstrumentedAppDir = null;
-    String mInstrumentedAppLibraryDir = null;
+    String[] mInstrumentedSplitAppDirs = null;
+    String mInstrumentedLibDir = null;
     boolean mSystemThread = false;
     boolean mJitEnabled = false;
 
@@ -317,7 +319,7 @@
         }
 
         public boolean isPersistable() {
-            return (activityInfo.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+            return activityInfo.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS;
         }
 
         public String toString() {
@@ -1585,11 +1587,11 @@
     /**
      * Creates the top level resources for the given package.
      */
-    Resources getTopLevelResources(String resDir, String[] overlayDirs, String[] libDirs,
-            int displayId, Configuration overrideConfiguration,
+    Resources getTopLevelResources(String resDir, String[] splitResDirs, String[] overlayDirs,
+            String[] libDirs, int displayId, Configuration overrideConfiguration,
             LoadedApk pkgInfo) {
-        return mResourcesManager.getTopLevelResources(resDir, overlayDirs, libDirs, displayId,
-                overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
+        return mResourcesManager.getTopLevelResources(resDir, splitResDirs, overlayDirs, libDirs,
+                displayId, overrideConfiguration, pkgInfo.getCompatibilityInfo(), null);
     }
 
     final Handler getHandler() {
@@ -4315,16 +4317,20 @@
                     + data.instrumentationName);
             }
 
+            mInstrumentationPackageName = ii.packageName;
             mInstrumentationAppDir = ii.sourceDir;
-            mInstrumentationAppLibraryDir = ii.nativeLibraryDir;
-            mInstrumentationAppPackage = ii.packageName;
+            mInstrumentationSplitAppDirs = ii.splitSourceDirs;
+            mInstrumentationLibDir = ii.nativeLibraryDir;
             mInstrumentedAppDir = data.info.getAppDir();
-            mInstrumentedAppLibraryDir = data.info.getLibDir();
+            mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
+            mInstrumentedLibDir = data.info.getLibDir();
 
             ApplicationInfo instrApp = new ApplicationInfo();
             instrApp.packageName = ii.packageName;
             instrApp.sourceDir = ii.sourceDir;
             instrApp.publicSourceDir = ii.publicSourceDir;
+            instrApp.splitSourceDirs = ii.splitSourceDirs;
+            instrApp.splitPublicSourceDirs = ii.splitPublicSourceDirs;
             instrApp.dataDir = ii.dataDir;
             instrApp.nativeLibraryDir = ii.nativeLibraryDir;
             LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
diff --git a/core/java/android/app/ActivityTransitionCoordinator.java b/core/java/android/app/ActivityTransitionCoordinator.java
index c7030b0..c351cd5 100644
--- a/core/java/android/app/ActivityTransitionCoordinator.java
+++ b/core/java/android/app/ActivityTransitionCoordinator.java
@@ -233,6 +233,13 @@
         if (getViewsTransition() != null) {
             getDecor().captureTransitioningViews(mTransitioningViews);
             mTransitioningViews.removeAll(mSharedElements);
+            Rect r = new Rect();
+            for (int i = mTransitioningViews.size() - 1; i >= 0; i--) {
+                View view = mTransitioningViews.get(i);
+                if (!view.getGlobalVisibleRect(r)) {
+                    mTransitioningViews.remove(i);
+                }
+            }
         }
         setEpicenter();
     }
@@ -274,7 +281,7 @@
     public ArrayList<String> getMappedNames() {
         ArrayList<String> names = new ArrayList<String>(mSharedElements.size());
         for (int i = 0; i < mSharedElements.size(); i++) {
-            names.add(mSharedElements.get(i).getViewName());
+            names.add(mSharedElements.get(i).getTransitionName());
         }
         return names;
     }
@@ -351,9 +358,9 @@
                 String name = mAllSharedElementNames.get(i);
                 View sharedElement = sharedElements.get(name);
                 if (sharedElement != null) {
-                    if (sharedElement.getViewName() == null) {
+                    if (sharedElement.getTransitionName() == null) {
                         throw new IllegalArgumentException("Shared elements must have " +
-                                "non-null viewNames");
+                                "non-null transitionNames");
                     }
                     mSharedElementNames.add(name);
                     mSharedElements.add(sharedElement);
@@ -487,7 +494,7 @@
                 if (bitmap != null) {
                     snapshot.setBackground(new BitmapDrawable(resources, bitmap));
                 }
-                snapshot.setViewName(name);
+                snapshot.setTransitionName(name);
                 setSharedElementState(snapshot, name, state, parentLoc);
                 snapshots.add(snapshot);
             }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 84673d9..13b922c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -48,11 +48,13 @@
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserHandle;
+import android.os.UserManager;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.view.Display;
@@ -823,8 +825,10 @@
         if (app.packageName.equals("system")) {
             return mContext.mMainThread.getSystemContext().getResources();
         }
+        final boolean sameUid = (app.uid == Process.myUid());
         Resources r = mContext.mMainThread.getTopLevelResources(
-                app.uid == Process.myUid() ? app.sourceDir : app.publicSourceDir,
+                sameUid ? app.sourceDir : app.publicSourceDir,
+                sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
                 app.resourceDirs, null, Display.DEFAULT_DISPLAY, null, mContext.mPackageInfo);
         if (r != null) {
             return r;
@@ -1455,10 +1459,10 @@
      * @hide
      */
     @Override
-    public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable,
-            int sourceUserId, int targetUserId) {
+    public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId,
+            int flags) {
         try {
-            mPM.addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId);
+            mPM.addCrossProfileIntentFilter(filter, sourceUserId, targetUserId, flags);
         } catch (RemoteException e) {
             // Should never happen!
         }
@@ -1467,10 +1471,13 @@
     /**
      * @hide
      */
-    @Override
-    public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId,
-            int targetUserId) {
-        addCrossProfileIntentFilter(filter, removable, sourceUserId, targetUserId);
+    public void addCrossProfileIntentsForPackage(String packageName,
+            int sourceUserId, int targetUserId) {
+        try {
+            mPM.addCrossProfileIntentsForPackage(packageName, sourceUserId, targetUserId);
+        } catch (RemoteException e) {
+            // Should never happen!
+        }
     }
 
     /**
@@ -1489,8 +1496,9 @@
      * @hide
      */
     @Override
-    public void clearForwardingIntentFilters(int sourceUserId) {
-        clearCrossProfileIntentFilters(sourceUserId);
+    public Bitmap getUserIcon(int userId) {
+        UserManager um = UserManager.get(mContext);
+        return um.getUserIcon(userId);
     }
 
     private final ContextImpl mContext;
diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java
index ef4099f..5998d7a 100644
--- a/core/java/android/app/ApplicationThreadNative.java
+++ b/core/java/android/app/ApplicationThreadNative.java
@@ -1184,6 +1184,7 @@
         data.writeInt(level);
         mRemote.transact(SCHEDULE_TRIM_MEMORY_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
+        data.recycle();
     }
 
     public void dumpMemInfo(FileDescriptor fd, Debug.MemoryInfo mem, boolean checkin,
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 01a388f..4433a3a 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -581,13 +581,13 @@
 
     @Override
     public FragmentTransaction setSharedElement(View sharedElement, String name) {
-        String viewName = sharedElement.getViewName();
-        if (viewName == null) {
-            throw new IllegalArgumentException("Unique viewNames are required for all" +
+        String transitionName = sharedElement.getTransitionName();
+        if (transitionName == null) {
+            throw new IllegalArgumentException("Unique transitionNames are required for all" +
                     " sharedElements");
         }
         mSharedElementSourceNames = new ArrayList<String>(1);
-        mSharedElementSourceNames.add(viewName);
+        mSharedElementSourceNames.add(transitionName);
 
         mSharedElementTargetNames = new ArrayList<String>(1);
         mSharedElementTargetNames.add(name);
@@ -603,12 +603,12 @@
             ArrayList<String> sourceNames = new ArrayList<String>(sharedElements.length);
             ArrayList<String> targetNames = new ArrayList<String>(sharedElements.length);
             for (int i = 0; i < sharedElements.length; i++) {
-                String viewName = sharedElements[i].first.getViewName();
-                if (viewName == null) {
-                    throw new IllegalArgumentException("Unique viewNames are required for all" +
-                            " sharedElements");
+                String transitionName = sharedElements[i].first.getTransitionName();
+                if (transitionName == null) {
+                    throw new IllegalArgumentException("Unique transitionNames are required for all"
+                            + " sharedElements");
                 }
-                sourceNames.add(viewName);
+                sourceNames.add(transitionName);
                 targetNames.add(sharedElements[i].second);
             }
             mSharedElementSourceNames = sourceNames;
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index a42bd3b..425a140 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -513,6 +513,9 @@
                 public Object createService(ContextImpl ctx) {
                     IBinder b = ServiceManager.getService(POWER_SERVICE);
                     IPowerManager service = IPowerManager.Stub.asInterface(b);
+                    if (service == null) {
+                        Log.wtf(TAG, "Failed to get power manager service.");
+                    }
                     return new PowerManager(ctx.getOuterContext(),
                             service, ctx.mMainThread.getHandler());
                 }});
@@ -694,8 +697,8 @@
 
         registerService(FINGERPRINT_SERVICE, new ServiceFetcher() {
             public Object createService(ContextImpl ctx) {
-                IBinder b = ServiceManager.getService(FINGERPRINT_SERVICE);
-                IFingerprintService service = IFingerprintService.Stub.asInterface(b);
+                IBinder binder = ServiceManager.getService(FINGERPRINT_SERVICE);
+                IFingerprintService service = IFingerprintService.Stub.asInterface(binder);
                 return new FingerprintManager(ctx.getOuterContext(), service);
             }
         });
@@ -2190,10 +2193,10 @@
                     || overrideConfiguration != null
                     || (compatInfo != null && compatInfo.applicationScale
                             != resources.getCompatibilityInfo().applicationScale)) {
-                resources = mResourcesManager.getTopLevelResources(
-                        packageInfo.getResDir(), packageInfo.getOverlayDirs(),
-                        packageInfo.getApplicationInfo().sharedLibraryFiles,
-                        displayId, overrideConfiguration, compatInfo, activityToken);
+                resources = mResourcesManager.getTopLevelResources(packageInfo.getResDir(),
+                        packageInfo.getSplitResDirs(), packageInfo.getOverlayDirs(),
+                        packageInfo.getApplicationInfo().sharedLibraryFiles, displayId,
+                        overrideConfiguration, compatInfo, activityToken);
             }
         }
         mResources = resources;
diff --git a/core/java/android/app/EnterTransitionCoordinator.java b/core/java/android/app/EnterTransitionCoordinator.java
index 365cc8e..1d7a0ec 100644
--- a/core/java/android/app/EnterTransitionCoordinator.java
+++ b/core/java/android/app/EnterTransitionCoordinator.java
@@ -55,6 +55,7 @@
     private boolean mIsExitTransitionComplete;
     private boolean mIsReadyForTransition;
     private Bundle mSharedElementsBundle;
+    private boolean mWasOpaque;
 
     public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
             ArrayList<String> sharedElementNames, boolean isReturning) {
@@ -191,7 +192,7 @@
     protected void prepareEnter() {
         mActivity.overridePendingTransition(0, 0);
         if (!mIsReturning) {
-            mActivity.convertToTranslucent(null, null);
+            mWasOpaque = mActivity.convertToTranslucent(null, null);
             Drawable background = getDecor().getBackground();
             if (background != null) {
                 getWindow().setBackgroundDrawable(null);
@@ -376,7 +377,9 @@
 
     private void makeOpaque() {
         if (!mHasStopped && mActivity != null) {
-            mActivity.convertFromTranslucent();
+            if (mWasOpaque) {
+                mActivity.convertFromTranslucent();
+            }
             mActivity = null;
         }
     }
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 6c0d379..2ff3d57 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -23,6 +23,7 @@
 import android.content.Intent;
 import android.content.res.Configuration;
 import android.content.res.Resources;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -1104,7 +1105,15 @@
      * inflation.  Maybe this should become a public API. Note sure.
      */
     public LayoutInflater getLayoutInflater(Bundle savedInstanceState) {
-        return mActivity.getLayoutInflater();
+        // Newer platform versions use the child fragment manager's LayoutInflaterFactory.
+        if (mActivity.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
+            LayoutInflater result = mActivity.getLayoutInflater().cloneInContext(mActivity);
+            getChildFragmentManager(); // Init if needed; use raw implementation below.
+            result.setPrivateFactory(mChildFragmentManager.getLayoutInflaterFactory());
+            return result;
+        } else {
+            return mActivity.getLayoutInflater();
+        }
     }
     
     /**
diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java
index b8f1962..a97fa650 100644
--- a/core/java/android/app/FragmentManager.java
+++ b/core/java/android/app/FragmentManager.java
@@ -19,6 +19,7 @@
 import android.animation.Animator;
 import android.animation.AnimatorInflater;
 import android.animation.AnimatorListenerAdapter;
+import android.content.Context;
 import android.content.res.Configuration;
 import android.content.res.TypedArray;
 import android.os.Bundle;
@@ -27,11 +28,13 @@
 import android.os.Looper;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.util.AttributeSet;
 import android.util.DebugUtils;
 import android.util.Log;
 import android.util.LogWriter;
 import android.util.SparseArray;
 import android.util.SuperNotCalledException;
+import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
@@ -397,7 +400,7 @@
 /**
  * Container for fragments associated with an activity.
  */
-final class FragmentManagerImpl extends FragmentManager {
+final class FragmentManagerImpl extends FragmentManager implements LayoutInflater.Factory2 {
     static boolean DEBUG = false;
     static final String TAG = "FragmentManager";
     
@@ -432,7 +435,7 @@
     boolean mDestroyed;
     String mNoTransactionsBecause;
     boolean mHavePendingDeferredStart;
-    
+
     // Temporary vars for state save and restore.
     Bundle mStateBundle = null;
     SparseArray<Parcelable> mStateArray = null;
@@ -2052,4 +2055,100 @@
         }
         return animAttr;
     }
+
+    @Override
+    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {
+        if (!"fragment".equals(name)) {
+            return null;
+        }
+
+        String fname = attrs.getAttributeValue(null, "class");
+        TypedArray a =
+                context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Fragment);
+        if (fname == null) {
+            fname = a.getString(com.android.internal.R.styleable.Fragment_name);
+        }
+        int id = a.getResourceId(com.android.internal.R.styleable.Fragment_id, View.NO_ID);
+        String tag = a.getString(com.android.internal.R.styleable.Fragment_tag);
+        a.recycle();
+
+        int containerId = parent != null ? parent.getId() : 0;
+        if (containerId == View.NO_ID && id == View.NO_ID && tag == null) {
+            throw new IllegalArgumentException(attrs.getPositionDescription()
+                    + ": Must specify unique android:id, android:tag, or have a parent with"
+                    + " an id for " + fname);
+        }
+
+        // If we restored from a previous state, we may already have
+        // instantiated this fragment from the state and should use
+        // that instance instead of making a new one.
+        Fragment fragment = id != View.NO_ID ? findFragmentById(id) : null;
+        if (fragment == null && tag != null) {
+            fragment = findFragmentByTag(tag);
+        }
+        if (fragment == null && containerId != View.NO_ID) {
+            fragment = findFragmentById(containerId);
+        }
+
+        if (FragmentManagerImpl.DEBUG) Log.v(TAG, "onCreateView: id=0x"
+                + Integer.toHexString(id) + " fname=" + fname
+                + " existing=" + fragment);
+        if (fragment == null) {
+            fragment = Fragment.instantiate(context, fname);
+            fragment.mFromLayout = true;
+            fragment.mFragmentId = id != 0 ? id : containerId;
+            fragment.mContainerId = containerId;
+            fragment.mTag = tag;
+            fragment.mInLayout = true;
+            fragment.mFragmentManager = this;
+            fragment.onInflate(mActivity, attrs, fragment.mSavedFragmentState);
+            addFragment(fragment, true);
+        } else if (fragment.mInLayout) {
+            // A fragment already exists and it is not one we restored from
+            // previous state.
+            throw new IllegalArgumentException(attrs.getPositionDescription()
+                    + ": Duplicate id 0x" + Integer.toHexString(id)
+                    + ", tag " + tag + ", or parent id 0x" + Integer.toHexString(containerId)
+                    + " with another fragment for " + fname);
+        } else {
+            // This fragment was retained from a previous instance; get it
+            // going now.
+            fragment.mInLayout = true;
+            // If this fragment is newly instantiated (either right now, or
+            // from last saved state), then give it the attributes to
+            // initialize itself.
+            if (!fragment.mRetaining) {
+                fragment.onInflate(mActivity, attrs, fragment.mSavedFragmentState);
+            }
+        }
+
+        // If we haven't finished entering the CREATED state ourselves yet,
+        // push the inflated child fragment along.
+        if (mCurState < Fragment.CREATED && fragment.mFromLayout) {
+            moveToState(fragment, Fragment.CREATED, 0, 0, false);
+        } else {
+            moveToState(fragment);
+        }
+
+        if (fragment.mView == null) {
+            throw new IllegalStateException("Fragment " + fname
+                    + " did not create a view.");
+        }
+        if (id != 0) {
+            fragment.mView.setId(id);
+        }
+        if (fragment.mView.getTag() == null) {
+            fragment.mView.setTag(tag);
+        }
+        return fragment.mView;
+    }
+
+    @Override
+    public View onCreateView(String name, Context context, AttributeSet attrs) {
+        return null;
+    }
+
+    LayoutInflater.Factory2 getLayoutInflaterFactory() {
+        return this;
+    }
 }
diff --git a/core/java/android/app/FragmentTransaction.java b/core/java/android/app/FragmentTransaction.java
index 7479ecd..b6ea3c3 100644
--- a/core/java/android/app/FragmentTransaction.java
+++ b/core/java/android/app/FragmentTransaction.java
@@ -184,10 +184,10 @@
     /**
      * Used with {@link #setCustomTransition(int, int)} to map a View from a removed or hidden
      * Fragment to a View from a shown or added Fragment.
-     * <var>sharedElement</var> must have a unique viewName in the View hierarchy.
+     * <var>sharedElement</var> must have a unique transitionName in the View hierarchy.
      * @param sharedElement A View in a disappearing Fragment to match with a View in an
      *                      appearing Fragment.
-     * @param name The viewName for a View in an appearing Fragment to match to the shared
+     * @param name The transitionName for a View in an appearing Fragment to match to the shared
      *             element.
      */
     public abstract FragmentTransaction setSharedElement(View sharedElement, String name);
@@ -195,8 +195,8 @@
     /**
      * Used with {@link #setCustomTransition(int, int)} to map multiple Views from removed or hidden
      * Fragments to a Views from a shown or added Fragments. Views in
-     * <var>sharedElements</var> must have unique viewNames in the View hierarchy.
-     * @param sharedElements Pairs of Views in disappearing Fragments to viewNames in
+     * <var>sharedElements</var> must have unique transitionNames in the View hierarchy.
+     * @param sharedElements Pairs of Views in disappearing Fragments to transitionNames in
      *                       appearing Fragments.
      */
     public abstract FragmentTransaction setSharedElements(Pair<View, String>... sharedElements);
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index bf2d7e5..b630278 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -429,6 +429,9 @@
     public IBinder getHomeActivityToken() throws RemoteException;
 
     /** @hide */
+    public void startLockTaskModeOnCurrent() throws RemoteException;
+
+    /** @hide */
     public void startLockTaskMode(int taskId) throws RemoteException;
 
     /** @hide */
@@ -438,6 +441,9 @@
     public void stopLockTaskMode() throws RemoteException;
 
     /** @hide */
+    public void stopLockTaskModeOnCurrent() throws RemoteException;
+
+    /** @hide */
     public boolean isInLockTaskMode() throws RemoteException;
 
     /** @hide */
@@ -744,4 +750,6 @@
     int START_VOICE_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+218;
     int GET_ACTIVITY_OPTIONS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+219;
     int GET_APP_TASKS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+220;
+    int START_LOCK_TASK_BY_CURRENT = IBinder.FIRST_CALL_TRANSACTION+221;
+    int STOP_LOCK_TASK_BY_CURRENT = IBinder.FIRST_CALL_TRANSACTION+222;
 }
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index b78b9c9..c583998 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -188,6 +188,9 @@
             endPerformanceSnapshot();
         }
         if (mPerfMetrics != null) {
+            if (results == null) {
+                results = new Bundle();
+            }
             results.putAll(mPerfMetrics);
         }
         if (mUiAutomation != null) {
diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java
index 3ae8bfc..065e88d 100644
--- a/core/java/android/app/LoadedApk.java
+++ b/core/java/android/app/LoadedApk.java
@@ -16,8 +16,8 @@
 
 package android.app;
 
+import android.text.TextUtils;
 import android.util.ArrayMap;
-import com.android.internal.util.ArrayUtils;
 
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
@@ -52,6 +52,8 @@
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Enumeration;
 
 final class IntentReceiverLeaked extends AndroidRuntimeException {
@@ -79,6 +81,8 @@
     final String mPackageName;
     private final String mAppDir;
     private final String mResDir;
+    private final String[] mSplitAppDirs;
+    private final String[] mSplitResDirs;
     private final String[] mOverlayDirs;
     private final String[] mSharedLibraries;
     private final String mDataDir;
@@ -116,13 +120,14 @@
     public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
             CompatibilityInfo compatInfo, ClassLoader baseLoader,
             boolean securityViolation, boolean includeCode) {
+        final int myUid = Process.myUid();
         mActivityThread = activityThread;
         mApplicationInfo = aInfo;
         mPackageName = aInfo.packageName;
         mAppDir = aInfo.sourceDir;
-        final int myUid = Process.myUid();
-        mResDir = aInfo.uid == myUid ? aInfo.sourceDir
-                : aInfo.publicSourceDir;
+        mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
+        mSplitAppDirs = aInfo.splitSourceDirs;
+        mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
         mOverlayDirs = aInfo.resourceDirs;
         if (!UserHandle.isSameUser(aInfo.uid, myUid) && !Process.isIsolated()) {
             aInfo.dataDir = PackageManager.getDataDirForUser(UserHandle.getUserId(myUid),
@@ -149,6 +154,8 @@
         mPackageName = "android";
         mAppDir = null;
         mResDir = null;
+        mSplitAppDirs = null;
+        mSplitResDirs = null;
         mOverlayDirs = null;
         mSharedLibraries = null;
         mDataDir = null;
@@ -214,53 +221,6 @@
         return ai.sharedLibraryFiles;
     }
 
-    /**
-     * Combines two arrays (of library names) such that they are
-     * concatenated in order but are devoid of duplicates. The
-     * result is a single string with the names of the libraries
-     * separated by colons, or <code>null</code> if both lists
-     * were <code>null</code> or empty.
-     *
-     * @param list1 null-ok; the first list
-     * @param list2 null-ok; the second list
-     * @return null-ok; the combination
-     */
-    private static String combineLibs(String[] list1, String[] list2) {
-        StringBuilder result = new StringBuilder(300);
-        boolean first = true;
-
-        if (list1 != null) {
-            for (String s : list1) {
-                if (first) {
-                    first = false;
-                } else {
-                    result.append(':');
-                }
-                result.append(s);
-            }
-        }
-
-        // Only need to check for duplicates if list1 was non-empty.
-        boolean dupCheck = !first;
-
-        if (list2 != null) {
-            for (String s : list2) {
-                if (dupCheck && ArrayUtils.contains(list1, s)) {
-                    continue;
-                }
-
-                if (first) {
-                    first = false;
-                } else {
-                    result.append(':');
-                }
-                result.append(s);
-            }
-        }
-
-        return result.toString();
-    }
-
     public ClassLoader getClassLoader() {
         synchronized (this) {
             if (mClassLoader != null) {
@@ -268,8 +228,15 @@
             }
 
             if (mIncludeCode && !mPackageName.equals("android")) {
-                String zip = mAppDir;
-                String libraryPath = mLibDir;
+                final ArrayList<String> zipPaths = new ArrayList<>();
+                final ArrayList<String> libPaths = new ArrayList<>();
+
+                zipPaths.add(mAppDir);
+                if (mSplitAppDirs != null) {
+                    Collections.addAll(zipPaths, mSplitAppDirs);
+                }
+
+                libPaths.add(mLibDir);
 
                 /*
                  * The following is a bit of a hack to inject
@@ -280,50 +247,70 @@
                  * concatenation of both apps' shared library lists.
                  */
 
-                String instrumentationAppDir =
-                        mActivityThread.mInstrumentationAppDir;
-                String instrumentationAppLibraryDir =
-                        mActivityThread.mInstrumentationAppLibraryDir;
-                String instrumentationAppPackage =
-                        mActivityThread.mInstrumentationAppPackage;
-                String instrumentedAppDir =
-                        mActivityThread.mInstrumentedAppDir;
-                String instrumentedAppLibraryDir =
-                        mActivityThread.mInstrumentedAppLibraryDir;
+                String instrumentationPackageName = mActivityThread.mInstrumentationPackageName;
+                String instrumentationAppDir = mActivityThread.mInstrumentationAppDir;
+                String[] instrumentationSplitAppDirs = mActivityThread.mInstrumentationSplitAppDirs;
+                String instrumentationLibDir = mActivityThread.mInstrumentationLibDir;
+
+                String instrumentedAppDir = mActivityThread.mInstrumentedAppDir;
+                String[] instrumentedSplitAppDirs = mActivityThread.mInstrumentedSplitAppDirs;
+                String instrumentedLibDir = mActivityThread.mInstrumentedLibDir;
                 String[] instrumentationLibs = null;
 
                 if (mAppDir.equals(instrumentationAppDir)
                         || mAppDir.equals(instrumentedAppDir)) {
-                    zip = instrumentationAppDir + ":" + instrumentedAppDir;
-                    libraryPath = instrumentationAppLibraryDir + ":" + instrumentedAppLibraryDir;
-                    if (! instrumentedAppDir.equals(instrumentationAppDir)) {
-                        instrumentationLibs =
-                            getLibrariesFor(instrumentationAppPackage);
+                    zipPaths.clear();
+                    zipPaths.add(instrumentationAppDir);
+                    if (instrumentationSplitAppDirs != null) {
+                        Collections.addAll(zipPaths, instrumentationSplitAppDirs);
+                    }
+                    zipPaths.add(instrumentedAppDir);
+                    if (instrumentedSplitAppDirs != null) {
+                        Collections.addAll(zipPaths, instrumentedSplitAppDirs);
+                    }
+
+                    libPaths.clear();
+                    libPaths.add(instrumentationLibDir);
+                    libPaths.add(instrumentedLibDir);
+
+                    if (!instrumentedAppDir.equals(instrumentationAppDir)) {
+                        instrumentationLibs = getLibrariesFor(instrumentationPackageName);
                     }
                 }
 
-                if ((mSharedLibraries != null) ||
-                        (instrumentationLibs != null)) {
-                    zip =
-                        combineLibs(mSharedLibraries, instrumentationLibs)
-                        + ':' + zip;
+                if (mSharedLibraries != null) {
+                    for (String lib : mSharedLibraries) {
+                        if (!zipPaths.contains(lib)) {
+                            zipPaths.add(0, lib);
+                        }
+                    }
                 }
 
+                if (instrumentationLibs != null) {
+                    for (String lib : instrumentationLibs) {
+                        if (!zipPaths.contains(lib)) {
+                            zipPaths.add(0, lib);
+                        }
+                    }
+                }
+
+                final String zip = TextUtils.join(File.pathSeparator, zipPaths);
+                final String lib = TextUtils.join(File.pathSeparator, libPaths);
+
                 /*
                  * With all the combination done (if necessary, actually
                  * create the class loader.
                  */
 
                 if (ActivityThread.localLOGV)
-                    Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + libraryPath);
+                    Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + lib);
 
                 // Temporarily disable logging of disk reads on the Looper thread
                 // as this is early and necessary.
                 StrictMode.ThreadPolicy oldPolicy = StrictMode.allowThreadDiskReads();
 
-                mClassLoader =
-                    ApplicationLoaders.getDefault().getClassLoader(
-                        zip, libraryPath, mBaseClassLoader);
+                mClassLoader = ApplicationLoaders.getDefault().getClassLoader(zip, lib,
+                        mBaseClassLoader);
                 initializeJavaContextClassLoader();
 
                 StrictMode.setThreadPolicy(oldPolicy);
@@ -469,6 +456,14 @@
         return mResDir;
     }
 
+    public String[] getSplitAppDirs() {
+        return mSplitAppDirs;
+    }
+
+    public String[] getSplitResDirs() {
+        return mSplitResDirs;
+    }
+
     public String[] getOverlayDirs() {
         return mOverlayDirs;
     }
@@ -487,7 +482,7 @@
 
     public Resources getResources(ActivityThread mainThread) {
         if (mResources == null) {
-            mResources = mainThread.getTopLevelResources(mResDir, mOverlayDirs,
+            mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,
                     mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, null, this);
         }
         return mResources;
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index b94fd41..84f7e5f 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -91,7 +91,10 @@
      * Use the default notification sound. This will ignore any given
      * {@link #sound}.
      *
-
+     * <p>
+     * A notification that is noisy is more likely to be presented as a heads-up notification.
+     * </p>
+     *
      * @see #defaults
      */
 
@@ -102,6 +105,10 @@
      * {@link #vibrate}. Using phone vibration requires the
      * {@link android.Manifest.permission#VIBRATE VIBRATE} permission.
      *
+     * <p>
+     * A notification that vibrates is more likely to be presented as a heads-up notification.
+     * </p>
+     *
      * @see #defaults
      */
 
@@ -195,6 +202,11 @@
     /**
      * An intent to launch instead of posting the notification to the status bar.
      *
+     * <p>
+     * The system UI may choose to display a heads-up notification, instead of
+     * launching this intent, while the user is using the device.
+     * </p>
+     *
      * @see Notification.Builder#setFullScreenIntent
      */
     public PendingIntent fullScreenIntent;
@@ -228,9 +240,10 @@
 
     /**
      * @hide
-     * A medium-format version of {@link #contentView}, giving the Notification an
-     * opportunity to add action buttons to contentView. The system UI may
-     * choose to show this as a popup notification at its discretion.
+     * A medium-format version of {@link #contentView}, providing the Notification an
+     * opportunity to add action buttons to contentView. At its discretion, the system UI may
+     * choose to show this as a heads-up notification, which will pop up so the user can see
+     * it without leaving their current activity.
      */
     public RemoteViews headsUpContentView;
 
@@ -243,6 +256,10 @@
      * The sound to play.
      *
      * <p>
+     * A notification that is noisy is more likely to be presented as a heads-up notification.
+     * </p>
+     *
+     * <p>
      * To play the default notification sound, see {@link #defaults}.
      * </p>
      */
@@ -269,6 +286,10 @@
      * To vibrate the default pattern, see {@link #defaults}.
      * </p>
      *
+     * <p>
+     * A notification that vibrates is more likely to be presented as a heads-up notification.
+     * </p>
+     *
      * @see android.os.Vibrator#vibrate(long[],int)
      */
     public long[] vibrate;
@@ -443,6 +464,12 @@
      * situations, while the user might be interrupted for a higher-priority notification. The
      * system will make a determination about how to interpret this priority when presenting
      * the notification.
+     *
+     * <p>
+     * A notification that is at least {@link #PRIORITY_HIGH} is more likely to be presented
+     * as a heads-up notification.
+     * </p>
+     *
      */
     @Priority
     public int priority;
@@ -724,7 +751,8 @@
     public static final String EXTRA_SCORE_MODIFIED = "android.scoreModified";
 
     /**
-     * Not used.
+     * {@link #extras} key: used to provide hints about the appropriateness of
+     * displaying this notification as a heads-up notification.
      * @hide
      */
     public static final String EXTRA_AS_HEADS_UP = "headsup";
@@ -755,19 +783,27 @@
     public static final String EXTRA_MEDIA_SESSION = "android.mediaSession";
 
     /**
-     * Value for {@link #EXTRA_AS_HEADS_UP}.
+     * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification should not be
+     * displayed in the heads up space.
+     *
+     * <p>
+     * If this notification has a {@link #fullScreenIntent}, then it will always launch the
+     * full-screen intent when posted.
+     * </p>
      * @hide
      */
     public static final int HEADS_UP_NEVER = 0;
 
     /**
-     * Default value for {@link #EXTRA_AS_HEADS_UP}.
+     * Default value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification may be
+     * displayed as a heads up.
      * @hide
      */
     public static final int HEADS_UP_ALLOWED = 1;
 
     /**
-     * Value for {@link #EXTRA_AS_HEADS_UP}.
+     * Value for {@link #EXTRA_AS_HEADS_UP} that indicates this notification is a
+     * good candidate for display as a heads up.
      * @hide
      */
     public static final int HEADS_UP_REQUESTED = 2;
@@ -1903,6 +1939,11 @@
          * to turn it off and use a normal notification, as this can be extremely
          * disruptive.
          *
+         * <p>
+         * The system UI may choose to display a heads-up notification, instead of
+         * launching this intent, while the user is using the device.
+         * </p>
+         *
          * @param intent The pending intent to launch.
          * @param highPriority Passing true will cause this notification to be sent
          *          even if other notifications are suppressed.
@@ -1958,6 +1999,10 @@
          *
          * It will be played on the {@link #STREAM_DEFAULT default stream} for notifications.
          *
+         * <p>
+         * A notification that is noisy is more likely to be presented as a heads-up notification.
+         * </p>
+         *
          * @see Notification#sound
          */
         public Builder setSound(Uri sound) {
@@ -1971,6 +2016,10 @@
          *
          * See {@link android.media.AudioManager} for the <code>STREAM_</code> constants.
          *
+         * <p>
+         * A notification that is noisy is more likely to be presented as a heads-up notification.
+         * </p>
+         *
          * @see Notification#sound
          */
         public Builder setSound(Uri sound, int streamType) {
@@ -1982,11 +2031,13 @@
         /**
          * Set the vibration pattern to use.
          *
-
          * See {@link android.os.Vibrator#vibrate(long[], int)} for a discussion of the
          * <code>pattern</code> parameter.
          *
-
+         * <p>
+         * A notification that vibrates is more likely to be presented as a heads-up notification.
+         * </p>
+         *
          * @see Notification#vibrate
          */
         public Builder setVibrate(long[] pattern) {
diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java
index a67faa0..3c13115 100644
--- a/core/java/android/app/ResourcesManager.java
+++ b/core/java/android/app/ResourcesManager.java
@@ -149,9 +149,9 @@
      * @param compatInfo the compability info. Must not be null.
      * @param token the application token for determining stack bounds.
      */
-    public Resources getTopLevelResources(String resDir, String[] overlayDirs, String[] libDirs,
-            int displayId, Configuration overrideConfiguration, CompatibilityInfo compatInfo,
-            IBinder token) {
+    public Resources getTopLevelResources(String resDir, String[] splitResDirs,
+            String[] overlayDirs, String[] libDirs, int displayId,
+            Configuration overrideConfiguration, CompatibilityInfo compatInfo, IBinder token) {
         final float scale = compatInfo.applicationScale;
         ResourcesKey key = new ResourcesKey(resDir, displayId, overrideConfiguration, scale, token);
         Resources r;
@@ -182,6 +182,14 @@
             return null;
         }
 
+        if (splitResDirs != null) {
+            for (String splitResDir : splitResDirs) {
+                if (assets.addAssetPath(splitResDir) == 0) {
+                    return null;
+                }
+            }
+        }
+
         if (overlayDirs != null) {
             for (String idmapPath : overlayDirs) {
                 assets.addOverlayPath(idmapPath);
diff --git a/core/java/android/app/SharedElementListener.java b/core/java/android/app/SharedElementListener.java
index e03e42e..14fbfab 100644
--- a/core/java/android/app/SharedElementListener.java
+++ b/core/java/android/app/SharedElementListener.java
@@ -108,7 +108,7 @@
      * @param names The names of all shared elements transferred from the calling Activity
      *              to the started Activity.
      * @param sharedElements The mapping of shared element names to Views. The best guess
-     *                       will be filled into sharedElements based on the View names.
+     *                       will be filled into sharedElements based on the transitionNames.
      */
     public void remapSharedElements(List<String> names, Map<String, View> sharedElements) {}
 }
diff --git a/core/java/android/app/admin/DeviceAdminReceiver.java b/core/java/android/app/admin/DeviceAdminReceiver.java
index 45a2625..ca40436 100644
--- a/core/java/android/app/admin/DeviceAdminReceiver.java
+++ b/core/java/android/app/admin/DeviceAdminReceiver.java
@@ -166,6 +166,40 @@
             = "android.app.action.ACTION_PASSWORD_EXPIRING";
 
     /**
+     * Action sent to a device administrator to notify that the device is entering
+     * or exiting lock task mode from an authorized package.  The extra
+     * {@link #EXTRA_LOCK_TASK_ENTERING} will describe whether entering or exiting
+     * the mode.  If entering, the extra {@link #EXTRA_LOCK_TASK_PACKAGE} will describe
+     * the authorized package using lock task mode.
+     *
+     * @see DevicePolicyManager#isLockTaskPermitted
+     *
+     * <p>The calling device admin must be the device owner or profile
+     * owner to receive this broadcast.
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_LOCK_TASK_CHANGED
+            = "android.app.action.ACTION_LOCK_TASK_CHANGED";
+
+    /**
+     * A boolean describing whether the device is currently entering or exiting
+     * lock task mode.
+     *
+     * @see #ACTION_LOCK_TASK_CHANGED
+     */
+    public static final String EXTRA_LOCK_TASK_ENTERING =
+            "android.app.extra.LOCK_TASK_ENTERING";
+
+    /**
+     * A boolean describing whether the device is currently entering or exiting
+     * lock task mode.
+     *
+     * @see #ACTION_LOCK_TASK_CHANGED
+     */
+    public static final String EXTRA_LOCK_TASK_PACKAGE =
+            "android.app.extra.LOCK_TASK_PACKAGE";
+
+    /**
      * Broadcast Action: This broadcast is sent to indicate that provisioning of a managed profile
      * or managed device has completed successfully.
      *
@@ -341,6 +375,19 @@
     }
 
     /**
+     * Called when a device is entering or exiting lock task mode by a package
+     * authorized by {@link DevicePolicyManager#isLockTaskPermitted(String)}
+     *
+     * @param context The running context as per {@link #onReceive}.
+     * @param intent The received intent as per {@link #onReceive}.
+     * @param isEnteringLockTask Whether the device is entering or exiting lock task mode.
+     * @param pkg If entering, the authorized package using lock task mode, otherwise null.
+     */
+    public void onLockTaskModeChanged(Context context, Intent intent, boolean isEnteringLockTask,
+            String pkg) {
+    }
+
+    /**
      * Intercept standard device administrator broadcasts.  Implementations
      * should not override this method; it is better to implement the
      * convenience callbacks for each action.
@@ -369,6 +416,10 @@
             onPasswordExpiring(context, intent);
         } else if (ACTION_PROFILE_PROVISIONING_COMPLETE.equals(action)) {
             onProfileProvisioningComplete(context, intent);
+        } else if (ACTION_LOCK_TASK_CHANGED.equals(action)) {
+            boolean isEntering = intent.getBooleanExtra(EXTRA_LOCK_TASK_ENTERING, false);
+            String pkg = intent.getStringExtra(EXTRA_LOCK_TASK_PACKAGE);
+            onLockTaskModeChanged(context, intent, isEntering, pkg);
         }
     }
 }
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 99f68d0..4351f9d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -18,6 +18,7 @@
 
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
+import android.app.Activity;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
@@ -1840,11 +1841,13 @@
      * This function should be used cautiously as once it is called it cannot
      * be undone.  The device owner can only be set as a part of device setup
      * before setup completes.
+     *
+     * @param packageName The package name of the device owner.
      */
-    public void clearDeviceOwnerApp() {
+    public void clearDeviceOwnerApp(String packageName) {
         if (mService != null) {
             try {
-                mService.clearDeviceOwner(mContext.getPackageName());
+                mService.clearDeviceOwner(packageName);
             } catch (RemoteException re) {
                 Log.w(TAG, "Failed to clear device owner");
             }
@@ -2341,15 +2344,20 @@
     }
 
     /**
-     * Sets which components may enter lock task mode.
+     * Sets which packages may enter lock task mode.
      *
-     * This function can only be called by the device owner or the profile owner.
-     * @param components The list of components allowed to enter lock task mode
+     * <p>Any packages that shares uid with an allowed package will also be allowed
+     * to activate lock task.
+     *
+     * This function can only be called by the device owner.
+     * @param packages The list of packages allowed to enter lock task mode
+     *
+     * @see Activity#startLockTask()
      */
-    public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+    public void setLockTaskPackages(String[] packages) throws SecurityException {
         if (mService != null) {
             try {
-                mService.setLockTaskComponents(components);
+                mService.setLockTaskPackages(packages);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2357,13 +2365,13 @@
     }
 
     /**
-     * This function returns the list of components allowed to start the lock task mode.
+     * This function returns the list of packages allowed to start the lock task mode.
      * @hide
      */
-    public ComponentName[] getLockTaskComponents() {
+    public String[] getLockTaskPackages() {
         if (mService != null) {
             try {
-                return mService.getLockTaskComponents();
+                return mService.getLockTaskPackages();
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
@@ -2374,12 +2382,12 @@
     /**
      * This function lets the caller know whether the given component is allowed to start the
      * lock task mode.
-     * @param component The component to check
+     * @param pkg The package to check
      */
-    public boolean isLockTaskPermitted(ComponentName component) {
+    public boolean isLockTaskPermitted(String pkg) {
         if (mService != null) {
             try {
-                return mService.isLockTaskPermitted(component);
+                return mService.isLockTaskPermitted(pkg);
             } catch (RemoteException e) {
                 Log.w(TAG, "Failed talking with device policy service", e);
             }
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index e935da7..5333ea6 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -142,13 +142,15 @@
     void setAccountManagementDisabled(in ComponentName who, in String accountType, in boolean disabled);
     String[] getAccountTypesWithManagementDisabled();
 
-    void setLockTaskComponents(in ComponentName[] components);
-    ComponentName[] getLockTaskComponents();
-    boolean isLockTaskPermitted(in ComponentName component);
+    void setLockTaskPackages(in String[] packages);
+    String[] getLockTaskPackages();
+    boolean isLockTaskPermitted(in String pkg);
 
     void setGlobalSetting(in ComponentName who, in String setting, in String value);
     void setSecureSetting(in ComponentName who, in String setting, in String value);
 
     void setMasterVolumeMuted(in ComponentName admin, boolean on);
     boolean isMasterVolumeMuted(in ComponentName admin);
+
+    void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userId);
 }
diff --git a/core/java/android/app/backup/BackupTransport.java b/core/java/android/app/backup/BackupTransport.java
index 56a55fb..4631323 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -31,12 +31,22 @@
  * @hide
  */
 public class BackupTransport {
+    // Zero return always means things are okay.  If returned from
+    // getNextFullRestoreDataChunk(), it means that no data could be delivered at
+    // this time, but the restore is still running and the caller should simply
+    // retry.
     public static final int TRANSPORT_OK = 0;
-    public static final int TRANSPORT_ERROR = 1;
-    public static final int TRANSPORT_NOT_INITIALIZED = 2;
-    public static final int TRANSPORT_PACKAGE_REJECTED = 3;
-    public static final int AGENT_ERROR = 4;
-    public static final int AGENT_UNKNOWN = 5;
+
+    // -1 is special; it is used in getNextFullRestoreDataChunk() to indicate that
+    // we've delivered the entire data stream for the current restore target.
+    public static final int NO_MORE_DATA = -1;
+
+    // Result codes that indicate real errors are negative and not -1
+    public static final int TRANSPORT_ERROR = -1000;
+    public static final int TRANSPORT_NOT_INITIALIZED = -1001;
+    public static final int TRANSPORT_PACKAGE_REJECTED = -1002;
+    public static final int AGENT_ERROR = -1003;
+    public static final int AGENT_UNKNOWN = -1004;
 
     IBackupTransport mBinderImpl = new TransportImpl();
     /** @hide */
@@ -228,19 +238,35 @@
     }
 
     /**
-     * Get the package name of the next application with data in the backup store.
+     * Get the package name of the next application with data in the backup store, plus
+     * a description of the structure of the restored archive: either TYPE_KEY_VALUE for
+     * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream.
      *
-     * @return The name of one of the packages supplied to {@link #startRestore},
-     *   or "" (the empty string) if no more backup data is available,
-     *   or null if an error occurred (the restore should be aborted and rescheduled).
+     * <p>If the package name in the returned RestoreDescription object is the singleton
+     * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available
+     * in the current restore session: all packages described in startRestore() have been
+     * processed.
+     *
+     * <p>If this method returns {@code null}, it means that a transport-level error has
+     * occurred and the entire restore operation should be abandoned.
+     *
+     * @return A RestoreDescription object containing the name of one of the packages
+     *   supplied to {@link #startRestore} plus an indicator of the data type of that
+     *   restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that
+     *   no more packages can be restored in this session; or {@code null} to indicate
+     *   a transport-level error.
      */
-    public String nextRestorePackage() {
+    public RestoreDescription nextRestorePackage() {
         return null;
     }
 
     /**
-     * Get the data for the application returned by {@link #nextRestorePackage}.
-     * @param data An open, writable file into which the backup data should be stored.
+     * Get the data for the application returned by {@link #nextRestorePackage}, if that
+     * method reported {@link RestoreDescription#TYPE_KEY_VALUE} as its delivery type.
+     * If the package has only TYPE_FULL_STREAM data, then this method will return an
+     * error.
+     *
+     * @param data An open, writable file into which the key/value backup data should be stored.
      * @return the same error codes as {@link #startRestore}.
      */
     public int getRestoreData(ParcelFileDescriptor outFd) {
@@ -332,32 +358,11 @@
     // Full restore interfaces
 
     /**
-     * Ask the transport to set up to perform a full data restore of the given packages.
+     * Ask the transport to provide data for the "current" package being restored.  This
+     * is the package that was just reported by {@link #nextRestorePackage()} as having
+     * {@link RestoreDescription#TYPE_FULL_STREAM} data.
      *
-     * @param token A backup token as returned by {@link #getAvailableRestoreSets}
-     *    or {@link #getCurrentRestoreSet}.
-     * @param targetPackage The names of the packages whose data is being requested.
-     * @return TRANSPORT_OK to indicate that the OS may proceed with requesting
-     *    restore data; TRANSPORT_ERROR to indicate a fatal error condition that precludes
-     *    performing any restore at this time.
-     */
-    public int prepareFullRestore(long token, String[] targetPackages) {
-        return BackupTransport.TRANSPORT_OK;
-    }
-
-    /**
-     * Ask the transport what package's full data will be restored next.  When all apps'
-     * data has been delivered, the transport should return {@code null} here.
-     * @return The package name of the next application whose data will be restored, or
-     *    {@code null} if all available package has been delivered.
-     */
-    public String getNextFullRestorePackage() {
-        return null;
-    }
-
-    /**
-     * Ask the transport to provide data for the "current" package being restored.  The
-     * transport then writes some data to the socket supplied to this call, and returns
+     * The transport writes some data to the socket supplied to this call, and returns
      * the number of bytes written.  The system will then read that many bytes and
      * stream them to the application's agent for restore, then will call this method again
      * to receive the next chunk of the archive.  This sequence will be repeated until the
@@ -369,17 +374,42 @@
      * {@link #getNextFullRestorePackage()} to begin the restore process for the next
      * application, and the sequence begins again.
      *
+     * <p>The transport should always close this socket when returning from this method.
+     * Do not cache this socket across multiple calls or you may leak file descriptors.
+     *
      * @param socket The file descriptor that the transport will use for delivering the
-     *    streamed archive.
-     * @return 0 when no more data for the current package is available.  A positive value
-     *    indicates the presence of that much data to be delivered to the app.  A negative
-     *    return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
-     *    indicating a fatal error condition that precludes further restore operations
-     *    on the current dataset.
+     *    streamed archive.  The transport must close this socket in all cases when returning
+     *    from this method.
+     * @return {@link #NO_MORE_DATA} when no more data for the current package is available.
+     *    A positive value indicates the presence of that many bytes to be delivered to the app.
+     *    A value of zero indicates that no data was deliverable at this time, but the restore
+     *    is still running and the caller should retry.  {@link #TRANSPORT_PACKAGE_REJECTED}
+     *    means that the current package's restore operation should be aborted, but that
+     *    the transport itself is still in a good state and so a multiple-package restore
+     *    sequence can still be continued.  Any other negative return value is treated as a
+     *    fatal error condition that aborts all further restore operations on the current dataset.
      */
     public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
         return 0;
     }
+
+    /**
+     * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
+     * data for restore, it will invoke this method to tell the transport that it should
+     * abandon the data download for the current package.  The OS will then either call
+     * {@link #nextRestorePackage()} again to move on to restoring the next package in the
+     * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
+     * operation.
+     *
+     * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
+     *    current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
+     *    transport-level failure.  If the transport reports an error here, the entire restore
+     *    operation will immediately be finished with no further attempts to restore app data.
+     */
+    public int abortFullRestore() {
+        return BackupTransport.TRANSPORT_OK;
+    }
+
     /**
      * Bridge between the actual IBackupTransport implementation and the stable API.  If the
      * binder interface needs to change, we use this layer to translate so that we can
@@ -450,7 +480,7 @@
         }
 
         @Override
-        public String nextRestorePackage() throws RemoteException {
+        public RestoreDescription nextRestorePackage() throws RemoteException {
             return BackupTransport.this.nextRestorePackage();
         }
 
@@ -478,5 +508,15 @@
         public int sendBackupData(int numBytes) throws RemoteException {
             return BackupTransport.this.sendBackupData(numBytes);
         }
+
+        @Override
+        public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
+            return BackupTransport.this.getNextFullRestoreDataChunk(socket);
+        }
+
+        @Override
+        public int abortFullRestore() {
+            return BackupTransport.this.abortFullRestore();
+        }
     }
 }
diff --git a/core/java/android/app/backup/RestoreDescription.aidl b/core/java/android/app/backup/RestoreDescription.aidl
new file mode 100644
index 0000000..9cbea78
--- /dev/null
+++ b/core/java/android/app/backup/RestoreDescription.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2014 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.backup;
+
+parcelable RestoreDescription;
diff --git a/core/java/android/app/backup/RestoreDescription.java b/core/java/android/app/backup/RestoreDescription.java
new file mode 100644
index 0000000..0fb4355
--- /dev/null
+++ b/core/java/android/app/backup/RestoreDescription.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 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.backup;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Description of the available restore data for a given package.  Returned by a
+ * BackupTransport in response to a request about the next available restorable
+ * package.
+ *
+ * @see BackupTransport#nextRestorePackage()
+ *
+ * @hide
+ */
+public class RestoreDescription implements Parcelable {
+    private final String mPackageName;
+    private final int mDataType;
+
+    private static final String NO_MORE_PACKAGES_SENTINEL = "";
+
+    /**
+     * Return this constant RestoreDescription from BackupTransport.nextRestorePackage()
+     * to indicate that no more package data is available in the current restore operation.
+     */
+    public static final RestoreDescription NO_MORE_PACKAGES =
+            new RestoreDescription(NO_MORE_PACKAGES_SENTINEL, 0);
+
+    // ---------------------------------------
+    // Data type identifiers
+
+    /** This package's restore data is an original-style key/value dataset */
+    public static final int TYPE_KEY_VALUE = 1;
+
+    /** This package's restore data is a tarball-type full data stream */
+    public static final int TYPE_FULL_STREAM = 2;
+
+    // ---------------------------------------
+    // API
+
+    public RestoreDescription(String packageName, int dataType) {
+        mPackageName = packageName;
+        mDataType = dataType;
+    }
+
+    public String getPackageName() {
+        return mPackageName;
+    }
+
+    public int getDataType() {
+        return mDataType;
+    }
+
+    // ---------------------------------------
+    // Parcelable implementation - not used by transport
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeString(mPackageName);
+        out.writeInt(mDataType);
+    }
+
+    public static final Parcelable.Creator<RestoreDescription> CREATOR
+            = new Parcelable.Creator<RestoreDescription>() {
+        public RestoreDescription createFromParcel(Parcel in) {
+            final RestoreDescription unparceled = new RestoreDescription(in);
+            return (NO_MORE_PACKAGES_SENTINEL.equals(unparceled.mPackageName))
+                    ? NO_MORE_PACKAGES
+                    : unparceled;
+        }
+
+        public RestoreDescription[] newArray(int size) {
+            return new RestoreDescription[size];
+        }
+    };
+
+    private RestoreDescription(Parcel in) {
+        mPackageName = in.readString();
+        mDataType = in.readInt();
+    }
+}
diff --git a/core/java/android/app/maintenance/IIdleCallback.aidl b/core/java/android/app/maintenance/IIdleCallback.aidl
deleted file mode 100644
index 582dede..0000000
--- a/core/java/android/app/maintenance/IIdleCallback.aidl
+++ /dev/null
@@ -1,53 +0,0 @@
-/**
- * Copyright 2014, 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.maintenance;
-
-import android.app.maintenance.IIdleService;
-
-/**
- * The server side of the idle maintenance IPC protocols.  The app-side implementation
- * invokes on this interface to indicate completion of the (asynchronous) instructions
- * issued by the server.
- *
- * In all cases, the 'who' parameter is the caller's service binder, used to track
- * which idle service instance is reporting.
- *
- * {@hide}
- */
-interface IIdleCallback {
-    /**
-     * Acknowledge receipt and processing of the asynchronous "start idle work" incall.
-     * 'result' is true if the app wants some time to perform ongoing background
-     * idle-time work; or false if the app declares that it does not need any time
-     * for such work.
-     */
-    void acknowledgeStart(int token, boolean result);
-
-    /**
-     * Acknowledge receipt and processing of the asynchronous "stop idle work" incall.
-     */
-    void acknowledgeStop(int token);
-
-    /*
-     * Tell the idle service manager that we're done with our idle maintenance, so that
-     * it can go on to the next one and stop attributing wakelock time to us etc.
-     *
-     * @param opToken The identifier passed in the startIdleMaintenance() call that
-     *        indicated the beginning of this service's idle timeslice.
-     */
-    void idleFinished(int token);
-}
diff --git a/core/java/android/app/maintenance/IIdleService.aidl b/core/java/android/app/maintenance/IIdleService.aidl
deleted file mode 100644
index 54abccd..0000000
--- a/core/java/android/app/maintenance/IIdleService.aidl
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Copyright 2014, 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.maintenance;
-
-import android.app.maintenance.IIdleCallback;
-
-/**
- * Interface that the framework uses to communicate with application code
- * that implements an idle-time "maintenance" service.  End user code does
- * not implement this interface directly; instead, the app's idle service
- * implementation will extend android.app.maintenance.IdleService.
- * {@hide}
- */
-oneway interface IIdleService {
-    /**
-     * Begin your idle-time work.
-     */
-    void startIdleMaintenance(IIdleCallback callbackBinder, int token);
-    void stopIdleMaintenance(IIdleCallback callbackBinder, int token);
-}
diff --git a/core/java/android/app/maintenance/IdleService.java b/core/java/android/app/maintenance/IdleService.java
deleted file mode 100644
index 2331b81..0000000
--- a/core/java/android/app/maintenance/IdleService.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * Copyright (C) 2014 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.maintenance;
-
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-import android.app.Service;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.util.Log;
-import android.util.Slog;
-
-/**
- * Idle maintenance API.  Full docs TBW (to be written).
- */
-public abstract class IdleService extends Service {
-    private static final String TAG = "IdleService";
-
-    static final int MSG_START = 1;
-    static final int MSG_STOP = 2;
-    static final int MSG_FINISH = 3;
-
-    IdleHandler mHandler;
-    IIdleCallback mCallbackBinder;
-    int mToken;
-    final Object mHandlerLock = new Object();
-
-    void ensureHandler() {
-        synchronized (mHandlerLock) {
-            if (mHandler == null) {
-                mHandler = new IdleHandler(getMainLooper());
-            }
-        }
-    }
-
-    /**
-     * TBW: the idle service should supply an intent-filter handling this intent
-     * <p>
-     * <p class="note">The application must also protect the idle service with the
-     * {@code "android.permission.BIND_IDLE_SERVICE"} permission to ensure that other
-     * applications cannot maliciously bind to it.  If an idle service's manifest
-     * declaration does not require that permission, it will never be invoked.
-     * </p>
-     */
-    @SdkConstant(SdkConstantType.SERVICE_ACTION)
-    public static final String SERVICE_INTERFACE =
-            "android.service.idle.IdleService";
-
-    /**
-     * Idle services must be protected with this permission:
-     *
-     * <pre class="prettyprint">
-     *     <service android:name="MyIdleService"
-     *              android:permission="android.permission.BIND_IDLE_SERVICE" >
-     *         ...
-     *     </service>
-     * </pre>
-     *
-     * <p>If an idle service is declared in the manifest but not protected with this
-     * permission, that service will be ignored by the OS.
-     */
-    public static final String PERMISSION_BIND =
-            "android.permission.BIND_IDLE_SERVICE";
-
-    // Trampoline: the callbacks are always run on the main thread
-    IIdleService mBinder = new IIdleService.Stub() {
-        @Override
-        public void startIdleMaintenance(IIdleCallback callbackBinder, int token)
-                throws RemoteException {
-            ensureHandler();
-            Message msg = mHandler.obtainMessage(MSG_START, token, 0, callbackBinder);
-            mHandler.sendMessage(msg);
-        }
-
-        @Override
-        public void stopIdleMaintenance(IIdleCallback callbackBinder, int token)
-                throws RemoteException {
-            ensureHandler();
-            Message msg = mHandler.obtainMessage(MSG_STOP, token, 0, callbackBinder);
-            mHandler.sendMessage(msg);
-        }
-    };
-
-    /**
-     * Your application may begin doing "idle" maintenance work in the background.
-     * <p>
-     * Your application may continue to run in the background until it receives a call
-     * to {@link #onIdleStop()}, at which point you <i>must</i> cease doing work.  The
-     * OS will hold a wakelock on your application's behalf from the time this method is
-     * called until after the following call to {@link #onIdleStop()} returns.
-     * </p>
-     * <p>
-     * Returning {@code false} from this method indicates that you have no ongoing work
-     * to do at present.  The OS will respond by immediately calling {@link #onIdleStop()}
-     * and returning your application to its normal stopped state.  Returning {@code true}
-     * indicates that the application is indeed performing ongoing work, so the OS will
-     * let your application run in this state until it's no longer appropriate.
-     * </p>
-     * <p>
-     * You will always receive a matching call to {@link #onIdleStop()} even if your
-     * application returns {@code false} from this method.
-     *
-     * @return {@code true} to indicate that the application wishes to perform some ongoing
-     *     background work; {@code false} to indicate that it does not need to perform such
-     *     work at present.
-     */
-    public abstract boolean onIdleStart();
-
-    /**
-     * Your app's maintenance opportunity is over.  Once the application returns from
-     * this method, the wakelock held by the OS on its behalf will be released.
-     */
-    public abstract void onIdleStop();
-
-    /**
-     * Tell the OS that you have finished your idle work.  Calling this more than once,
-     * or calling it when you have not received an {@link #onIdleStart()} callback, is
-     * an error.
-     *
-     * <p>It is safe to call {@link #finishIdle()} from any thread.
-     */
-    public final void finishIdle() {
-        ensureHandler();
-        mHandler.sendEmptyMessage(MSG_FINISH);
-    }
-
-    class IdleHandler extends Handler {
-        IdleHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_START: {
-                    // Call the concrete onIdleStart(), reporting its return value back to
-                    // the OS.  If onIdleStart() throws, report it as a 'false' return but
-                    // rethrow the exception at the offending app.
-                    boolean result = false;
-                    IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
-                    mCallbackBinder = callbackBinder;
-                    final int token = mToken = msg.arg1;
-                    try {
-                        result = IdleService.this.onIdleStart();
-                    } catch (Exception e) {
-                        Log.e(TAG, "Unable to start idle workload", e);
-                        throw new RuntimeException(e);
-                    } finally {
-                        // don't bother if the service already called finishIdle()
-                        if (mCallbackBinder != null) {
-                            try {
-                                callbackBinder.acknowledgeStart(token, result);
-                            } catch (RemoteException re) {
-                                Log.e(TAG, "System unreachable to start idle workload");
-                            }
-                        }
-                    }
-                    break;
-                }
-
-                case MSG_STOP: {
-                    // Structured just like MSG_START for the stop-idle bookend call.
-                    IIdleCallback callbackBinder = (IIdleCallback) msg.obj;
-                    final int token = msg.arg1;
-                    try {
-                        IdleService.this.onIdleStop();
-                    } catch (Exception e) {
-                        Log.e(TAG, "Unable to stop idle workload", e);
-                        throw new RuntimeException(e);
-                    } finally {
-                        if (mCallbackBinder != null) {
-                            try {
-                                callbackBinder.acknowledgeStop(token);
-                            } catch (RemoteException re) {
-                                Log.e(TAG, "System unreachable to stop idle workload");
-                            }
-                        }
-                    }
-                    break;
-                }
-
-                case MSG_FINISH: {
-                    if (mCallbackBinder != null) {
-                        try {
-                            mCallbackBinder.idleFinished(mToken);
-                        } catch (RemoteException e) {
-                            Log.e(TAG, "System unreachable to finish idling");
-                        } finally {
-                            mCallbackBinder = null;
-                        }
-                    } else {
-                        Log.e(TAG, "finishIdle() called but the idle service is not started");
-                    }
-                    break;
-                }
-
-                default: {
-                    Slog.w(TAG, "Unknown message " + msg.what);
-                }
-            }
-        }
-    }
-
-    /** @hide */
-    @Override
-    public final IBinder onBind(Intent intent) {
-        return mBinder.asBinder();
-    }
-
-}
diff --git a/core/java/android/app/maintenance/package.html b/core/java/android/app/maintenance/package.html
deleted file mode 100644
index 1c9bf9d..0000000
--- a/core/java/android/app/maintenance/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body>
-    {@hide}
-</body>
-</html>
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 6e53a6fb..e24dc84 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -55,6 +55,7 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Locale;
+import java.util.Objects;
 import java.util.Set;
 
 /**
@@ -6821,72 +6822,12 @@
         if (other == null) {
             return false;
         }
-        if (mAction != other.mAction) {
-            if (mAction != null) {
-                if (!mAction.equals(other.mAction)) {
-                    return false;
-                }
-            } else {
-                if (!other.mAction.equals(mAction)) {
-                    return false;
-                }
-            }
-        }
-        if (mData != other.mData) {
-            if (mData != null) {
-                if (!mData.equals(other.mData)) {
-                    return false;
-                }
-            } else {
-                if (!other.mData.equals(mData)) {
-                    return false;
-                }
-            }
-        }
-        if (mType != other.mType) {
-            if (mType != null) {
-                if (!mType.equals(other.mType)) {
-                    return false;
-                }
-            } else {
-                if (!other.mType.equals(mType)) {
-                    return false;
-                }
-            }
-        }
-        if (mPackage != other.mPackage) {
-            if (mPackage != null) {
-                if (!mPackage.equals(other.mPackage)) {
-                    return false;
-                }
-            } else {
-                if (!other.mPackage.equals(mPackage)) {
-                    return false;
-                }
-            }
-        }
-        if (mComponent != other.mComponent) {
-            if (mComponent != null) {
-                if (!mComponent.equals(other.mComponent)) {
-                    return false;
-                }
-            } else {
-                if (!other.mComponent.equals(mComponent)) {
-                    return false;
-                }
-            }
-        }
-        if (mCategories != other.mCategories) {
-            if (mCategories != null) {
-                if (!mCategories.equals(other.mCategories)) {
-                    return false;
-                }
-            } else {
-                if (!other.mCategories.equals(mCategories)) {
-                    return false;
-                }
-            }
-        }
+        if (!Objects.equals(this.mAction, other.mAction)) return false;
+        if (!Objects.equals(this.mData, other.mData)) return false;
+        if (!Objects.equals(this.mType, other.mType)) return false;
+        if (!Objects.equals(this.mPackage, other.mPackage)) return false;
+        if (!Objects.equals(this.mComponent, other.mComponent)) return false;
+        if (!Objects.equals(this.mCategories, other.mCategories)) return false;
 
         return true;
     }
@@ -7375,6 +7316,7 @@
             for (int categoryNdx = mCategories.size() - 1; categoryNdx >= 0; --categoryNdx) {
                 out.attribute(null, ATTR_CATEGORY, mCategories.valueAt(categoryNdx));
             }
+            out.endTag(null, TAG_CATEGORIES);
         }
     }
 
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 791e5aa..bcf1e87 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -104,6 +104,28 @@
     public int documentLaunchMode;
 
     /**
+     * Constant corresponding to <code>persistRootOnly</code> in
+     * the {@link android.R.attr#persistableMode} attribute.
+     */
+    public static final int PERSIST_ROOT_ONLY = 0;
+    /**
+     * Constant corresponding to <code>doNotPersist</code> in
+     * the {@link android.R.attr#persistableMode} attribute.
+     */
+    public static final int DO_NOT_PERSIST = 1;
+    /**
+     * Constant corresponding to <code>persistAcrossReboots</code> in
+     * the {@link android.R.attr#persistableMode} attribute.
+     */
+    public static final int PERSIST_ACROSS_REBOOTS = 2;
+    /**
+     * Value indicating how this activity is to be persisted across
+     * reboots for restoring in the Recents list.
+     * {@link android.R.attr#persistableMode}
+     */
+    public int persistableMode;
+
+    /**
      * The maximum number of tasks rooted at this activity that can be in the recent task list.
      * Refer to {@link android.R.attr#maxRecents}.
      */
@@ -230,11 +252,12 @@
      */
     public static final int FLAG_IMMERSIVE = 0x0800;
     /**
-     * Bit in {@link #flags} indicating that this activity is to be persisted across
-     * reboots for display in the Recents list.
-     * {@link android.R.attr#persistable}
+     * Bit in {@link #flags}: If set, a task rooted at this activity will have its
+     * baseIntent replaced by the activity immediately above this. Each activity may further
+     * relinquish its identity to the activity above it using this flag. Set from the
+     * android.R.attr#relinquishTaskIdentity attribute.
      */
-    public static final int FLAG_PERSISTABLE = 0x1000;
+    public static final int FLAG_RELINQUISH_TASK_IDENTITY = 0x1000;
     /**
      * Bit in {@link #flags} indicating that tasks started with this activity are to be
      * removed from the recent list of tasks when the last activity in the task is finished.
@@ -641,13 +664,23 @@
         return theme != 0 ? theme : applicationInfo.theme;
     }
 
+    private String persistableModeToString() {
+        switch(persistableMode) {
+            case PERSIST_ROOT_ONLY: return "PERSIST_ROOT_ONLY";
+            case DO_NOT_PERSIST: return "DO_NOT_PERSIST";
+            case PERSIST_ACROSS_REBOOTS: return "PERSIST_ACROSS_REBOOTS";
+            default: return "UNKNOWN=" + persistableMode;
+        }
+    }
+
     public void dump(Printer pw, String prefix) {
         super.dumpFront(pw, prefix);
         if (permission != null) {
             pw.println(prefix + "permission=" + permission);
         }
         pw.println(prefix + "taskAffinity=" + taskAffinity
-                + " targetActivity=" + targetActivity);
+                + " targetActivity=" + targetActivity
+                + " persistableMode=" + persistableModeToString());
         if (launchMode != 0 || flags != 0 || theme != 0) {
             pw.println(prefix + "launchMode=" + launchMode
                     + " flags=0x" + Integer.toHexString(flags)
@@ -688,6 +721,7 @@
         dest.writeInt(softInputMode);
         dest.writeInt(uiOptions);
         dest.writeString(parentActivityName);
+        dest.writeInt(persistableMode);
     }
 
     public static final Parcelable.Creator<ActivityInfo> CREATOR
@@ -713,5 +747,6 @@
         softInputMode = source.readInt();
         uiOptions = source.readInt();
         parentActivityName = source.readString();
+        persistableMode = source.readInt();
     }
 }
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 6b44a11..be4e864 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -23,8 +23,12 @@
 import android.os.Parcelable;
 import android.util.Printer;
 
+import com.android.internal.util.ArrayUtils;
+
 import java.text.Collator;
+import java.util.Arrays;
 import java.util.Comparator;
+import java.util.Objects;
 
 /**
  * Information you can retrieve about a particular application.  This
@@ -321,6 +325,15 @@
     public static final int FLAG_IS_GAME = 1<<25;
 
     /**
+     * Value for {@link #flags}: {@code true} if the application asks that only
+     * full-data streaming backups of its data be performed even though it defines
+     * a {@link android.app.backup.BackupAgent BackupAgent}, which normally
+     * indicates that the app will manage its backed-up data via incremental
+     * key/value updates.
+     */
+    public static final int FLAG_FULL_BACKUP_ONLY = 1<<26;
+
+    /**
      * Value for {@link #flags}: set to {@code true} if the application
      * is permitted to hold privileged permissions.
      *
@@ -398,17 +411,30 @@
     public int largestWidthLimitDp = 0;
 
     /**
-     * Full path to the location of this package.
+     * Full path to the base APK for this application.
      */
     public String sourceDir;
 
     /**
-     * Full path to the location of the publicly available parts of this
-     * package (i.e. the primary resource package and manifest).  For
-     * non-forward-locked apps this will be the same as {@link #sourceDir).
+     * Full path to the publicly available parts of {@link #sourceDir},
+     * including resources and manifest. This may be different from
+     * {@link #sourceDir} if an application is forward locked.
      */
     public String publicSourceDir;
-    
+
+    /**
+     * Full paths to zero or more split APKs that, when combined with the base
+     * APK defined in {@link #sourceDir}, form a complete application.
+     */
+    public String[] splitSourceDirs;
+
+    /**
+     * Full path to the publicly available parts of {@link #splitSourceDirs},
+     * including resources and manifest. This may be different from
+     * {@link #splitSourceDirs} if an application is forward locked.
+     */
+    public String[] splitPublicSourceDirs;
+
     /**
      * Full paths to the locations of extra resource packages this application
      * uses. This field is only used if there are extra resource packages,
@@ -512,13 +538,16 @@
                 + " compatibleWidthLimitDp=" + compatibleWidthLimitDp
                 + " largestWidthLimitDp=" + largestWidthLimitDp);
         pw.println(prefix + "sourceDir=" + sourceDir);
-        if (sourceDir == null) {
-            if (publicSourceDir != null) {
-                pw.println(prefix + "publicSourceDir=" + publicSourceDir);
-            }
-        } else if (!sourceDir.equals(publicSourceDir)) {
+        if (!Objects.equals(sourceDir, publicSourceDir)) {
             pw.println(prefix + "publicSourceDir=" + publicSourceDir);
         }
+        if (!ArrayUtils.isEmpty(splitSourceDirs)) {
+            pw.println(prefix + "splitSourceDirs=" + Arrays.toString(splitSourceDirs));
+        }
+        if (!ArrayUtils.isEmpty(splitPublicSourceDirs)
+                && !Arrays.equals(splitSourceDirs, splitPublicSourceDirs)) {
+            pw.println(prefix + "splitPublicSourceDirs=" + Arrays.toString(splitPublicSourceDirs));
+        }
         if (resourceDirs != null) {
             pw.println(prefix + "resourceDirs=" + resourceDirs);
         }
@@ -591,6 +620,8 @@
         largestWidthLimitDp = orig.largestWidthLimitDp;
         sourceDir = orig.sourceDir;
         publicSourceDir = orig.publicSourceDir;
+        splitSourceDirs = orig.splitSourceDirs;
+        splitPublicSourceDirs = orig.splitPublicSourceDirs;
         nativeLibraryDir = orig.nativeLibraryDir;
         cpuAbi = orig.cpuAbi;
         resourceDirs = orig.resourceDirs;
@@ -633,6 +664,8 @@
         dest.writeInt(largestWidthLimitDp);
         dest.writeString(sourceDir);
         dest.writeString(publicSourceDir);
+        dest.writeStringArray(splitSourceDirs);
+        dest.writeStringArray(splitPublicSourceDirs);
         dest.writeString(nativeLibraryDir);
         dest.writeString(cpuAbi);
         dest.writeStringArray(resourceDirs);
@@ -674,6 +707,8 @@
         largestWidthLimitDp = source.readInt();
         sourceDir = source.readString();
         publicSourceDir = source.readString();
+        splitSourceDirs = source.readStringArray();
+        splitPublicSourceDirs = source.readStringArray();
         nativeLibraryDir = source.readString();
         cpuAbi = source.readString();
         resourceDirs = source.readStringArray();
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 70668e1..6111b01 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -248,7 +248,10 @@
 
     void clearPackagePersistentPreferredActivities(String packageName, int userId);
 
-    void addCrossProfileIntentFilter(in IntentFilter filter, boolean removable, int sourceUserId,
+    void addCrossProfileIntentFilter(in IntentFilter intentFilter, int sourceUserId, int targetUserId,
+            int flags);
+
+    void addCrossProfileIntentsForPackage(in String packageName, int sourceUserId,
             int targetUserId);
 
     void clearCrossProfileIntentFilters(int sourceUserId);
@@ -438,7 +441,7 @@
             int flags, in String installerPackageName,
             in VerificationParams verificationParams,
             in ContainerEncryptionParams encryptionParams,
-	    in String packageAbiOverride);
+        in String packageAbiOverride);
 
     int installExistingPackageAsUser(String packageName, int userId);
 
diff --git a/core/java/android/content/pm/InstrumentationInfo.java b/core/java/android/content/pm/InstrumentationInfo.java
index a977e41..dab0caf 100644
--- a/core/java/android/content/pm/InstrumentationInfo.java
+++ b/core/java/android/content/pm/InstrumentationInfo.java
@@ -30,17 +30,32 @@
      * "package" attribute.
      */
     public String targetPackage;
-    
+
     /**
-     * Full path to the location of this package.
+     * Full path to the base APK for this application.
      */
     public String sourceDir;
-    
+
     /**
-     * Full path to the location of the publicly available parts of this package (i.e. the resources
-     * and manifest).  For non-forward-locked apps this will be the same as {@link #sourceDir).
+     * Full path to the publicly available parts of {@link #sourceDir},
+     * including resources and manifest. This may be different from
+     * {@link #sourceDir} if an application is forward locked.
      */
     public String publicSourceDir;
+
+    /**
+     * Full paths to zero or more split APKs that, when combined with the base
+     * APK defined in {@link #sourceDir}, form a complete application.
+     */
+    public String[] splitSourceDirs;
+
+    /**
+     * Full path to the publicly available parts of {@link #splitSourceDirs},
+     * including resources and manifest. This may be different from
+     * {@link #splitSourceDirs} if an application is forward locked.
+     */
+    public String[] splitPublicSourceDirs;
+
     /**
      * Full path to a directory assigned to the package for its persistent
      * data.
diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java
index 69fa408..6c10bb8 100644
--- a/core/java/android/content/pm/LauncherApps.java
+++ b/core/java/android/content/pm/LauncherApps.java
@@ -57,6 +57,65 @@
 
     private List<OnAppsChangedListener> mListeners
             = new ArrayList<OnAppsChangedListener>();
+    private List<OnAppsChangedCallback> mCallbacks
+            = new ArrayList<OnAppsChangedCallback>();
+
+    /**
+     * Callbacks for package changes to this and related managed profiles.
+     */
+    public static abstract class OnAppsChangedCallback {
+        /**
+         * Indicates that a package was removed from the specified profile.
+         *
+         * @param packageName The name of the package that was removed.
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        abstract public void onPackageRemoved(String packageName, UserHandle user);
+
+        /**
+         * Indicates that a package was added to the specified profile.
+         *
+         * @param packageName The name of the package that was added.
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        abstract public void onPackageAdded(String packageName, UserHandle user);
+
+        /**
+         * Indicates that a package was modified in the specified profile.
+         *
+         * @param packageName The name of the package that has changed.
+         * @param user The UserHandle of the profile that generated the change.
+         */
+        abstract public void onPackageChanged(String packageName, UserHandle user);
+
+        /**
+         * Indicates that one or more packages have become available. For
+         * example, this can happen when a removable storage card has
+         * reappeared.
+         *
+         * @param packageNames The names of the packages that have become
+         *            available.
+         * @param user The UserHandle of the profile that generated the change.
+         * @param replacing Indicates whether these packages are replacing
+         *            existing ones.
+         */
+        abstract public void onPackagesAvailable(String[] packageNames, UserHandle user,
+                boolean replacing);
+
+        /**
+         * Indicates that one or more packages have become unavailable. For
+         * example, this can happen when a removable storage card has been
+         * removed.
+         *
+         * @param packageNames The names of the packages that have become
+         *            unavailable.
+         * @param user The UserHandle of the profile that generated the change.
+         * @param replacing Indicates whether the packages are about to be
+         *            replaced with new versions.
+         */
+        abstract public void onPackagesUnavailable(String[] packageNames, UserHandle user,
+                boolean replacing);
+    }
 
     /**
      * Callbacks for package changes to this and related managed profiles.
@@ -270,7 +329,7 @@
         synchronized (this) {
             if (listener != null && !mListeners.contains(listener)) {
                 mListeners.add(listener);
-                if (mListeners.size() == 1) {
+                if (mListeners.size() == 1 && mCallbacks.size() == 0) {
                     try {
                         mService.addOnAppsChangedListener(mAppsChangedListener);
                     } catch (RemoteException re) {
@@ -289,7 +348,44 @@
     public void removeOnAppsChangedListener(OnAppsChangedListener listener) {
         synchronized (this) {
             mListeners.remove(listener);
-            if (mListeners.size() == 0) {
+            if (mListeners.size() == 0 && mCallbacks.size() == 0) {
+                try {
+                    mService.removeOnAppsChangedListener(mAppsChangedListener);
+                } catch (RemoteException re) {
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds a callback for changes to packages in current and managed profiles.
+     *
+     * @param callback The callback to add.
+     */
+    public void addOnAppsChangedCallback(OnAppsChangedCallback callback) {
+        synchronized (this) {
+            if (callback != null && !mCallbacks.contains(callback)) {
+                mCallbacks.add(callback);
+                if (mCallbacks.size() == 1 && mListeners.size() == 0) {
+                    try {
+                        mService.addOnAppsChangedListener(mAppsChangedListener);
+                    } catch (RemoteException re) {
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes a callback that was previously added.
+     *
+     * @param callback The callback to remove.
+     * @see #addOnAppsChangedListener(OnAppsChangedCallback)
+     */
+    public void removeOnAppsChangedCallback(OnAppsChangedCallback callback) {
+        synchronized (this) {
+            mListeners.remove(callback);
+            if (mListeners.size() == 0 && mCallbacks.size() == 0) {
                 try {
                     mService.removeOnAppsChangedListener(mAppsChangedListener);
                 } catch (RemoteException re) {
@@ -309,6 +405,9 @@
                 for (OnAppsChangedListener listener : mListeners) {
                     listener.onPackageRemoved(user, packageName);
                 }
+                for (OnAppsChangedCallback callback : mCallbacks) {
+                    callback.onPackageRemoved(packageName, user);
+                }
             }
         }
 
@@ -321,6 +420,9 @@
                 for (OnAppsChangedListener listener : mListeners) {
                     listener.onPackageChanged(user, packageName);
                 }
+                for (OnAppsChangedCallback callback : mCallbacks) {
+                    callback.onPackageChanged(packageName, user);
+                }
             }
         }
 
@@ -333,6 +435,9 @@
                 for (OnAppsChangedListener listener : mListeners) {
                     listener.onPackageAdded(user, packageName);
                 }
+                for (OnAppsChangedCallback callback : mCallbacks) {
+                    callback.onPackageAdded(packageName, user);
+                }
             }
         }
 
@@ -346,6 +451,9 @@
                 for (OnAppsChangedListener listener : mListeners) {
                     listener.onPackagesAvailable(user, packageNames, replacing);
                 }
+                for (OnAppsChangedCallback callback : mCallbacks) {
+                    callback.onPackagesAvailable(packageNames, user, replacing);
+                }
             }
         }
 
@@ -359,7 +467,10 @@
                 for (OnAppsChangedListener listener : mListeners) {
                     listener.onPackagesUnavailable(user, packageNames, replacing);
                 }
-            }
+                for (OnAppsChangedCallback callback : mCallbacks) {
+                    callback.onPackagesUnavailable(packageNames, user, replacing);
+                }
+           }
         }
     };
 }
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8d9b8d9..d11698c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -29,6 +29,7 @@
 import android.content.pm.PackageParser.PackageParserException;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Environment;
@@ -196,6 +197,22 @@
      */
     public static final int MATCH_DEFAULT_ONLY   = 0x00010000;
 
+    /**
+     * Flag for {@link addCrossProfileIntentFilter}: if the cross-profile intent has been set by the
+     * profile owner.
+     * @hide
+     */
+    public static final int SET_BY_PROFILE_OWNER= 0x00000001;
+
+    /**
+     * Flag for {@link addCrossProfileIntentFilter}: if this flag is set:
+     * when resolving an intent that matches the {@link CrossProfileIntentFilter}, the current
+     * profile will be skipped.
+     * Only activities in the target user can respond to the intent.
+     * @hide
+     */
+    public static final int SKIP_CURRENT_PROFILE = 0x00000002;
+
     /** @hide */
     @IntDef({PERMISSION_GRANTED, PERMISSION_DENIED})
     @Retention(RetentionPolicy.SOURCE)
@@ -2870,15 +2887,13 @@
      *
      */
     public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) {
-        PackageParser packageParser = new PackageParser(archiveFilePath);
-        DisplayMetrics metrics = new DisplayMetrics();
-        metrics.setToDefaults();
-        final File sourceFile = new File(archiveFilePath);
+        final PackageParser parser = new PackageParser();
+        final File apkFile = new File(archiveFilePath);
         try {
-            PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics,
-                    0);
+            PackageParser.Package pkg = parser.parseMonolithicPackage(apkFile, 0);
             if ((flags & GET_SIGNATURES) != 0) {
-                packageParser.collectCertificates(pkg, 0);
+                parser.collectCertificates(pkg, 0);
+                parser.collectManifestDigest(pkg);
             }
             PackageUserState state = new PackageUserState();
             return PackageParser.generatePackageInfo(pkg, null, flags, 0, 0, null, state);
@@ -3586,30 +3601,26 @@
      * {@link CrossProfileIntentFilter}
      * @hide
      */
-    public abstract void addCrossProfileIntentFilter(IntentFilter filter, boolean removable,
-            int sourceUserId, int targetUserId);
+    public abstract void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId,
+            int targetUserId, int flags);
 
     /**
-     * @hide
-     * @deprecated
-     * TODO: remove it as soon as the code of ManagedProvisionning is updated
-    */
-    public abstract void addForwardingIntentFilter(IntentFilter filter, boolean removable,
-            int sourceUserId, int targetUserId);
-
-    /**
-     * Clearing removable {@link CrossProfileIntentFilter}s which have the specified user as their
-     * source
+     * Clearing {@link CrossProfileIntentFilter}s which have the specified user as their
+     * source, and have been set by the profile owner
      * @param sourceUserId
-     * be cleared.
      * @hide
      */
     public abstract void clearCrossProfileIntentFilters(int sourceUserId);
 
     /**
+     * Forwards all intents for {@link packageName} for user {@link sourceUserId} to user
+     * {@link targetUserId}.
      * @hide
-     * @deprecated
-     * TODO: remove it as soon as the code of ManagedProvisionning is updated
-    */
-    public abstract void clearForwardingIntentFilters(int sourceUserId);
+     */
+    public abstract void addCrossProfileIntentsForPackage(String packageName,
+            int sourceUserId, int targetUserId);
+    /**
+     * @hide
+     */
+    public abstract Bitmap getUserIcon(int userId);
 }
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 91895ff..b40a441 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -16,9 +16,14 @@
 
 package android.content.pm;
 
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_MANIFEST;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
 import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
+import static android.content.pm.PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
 
 import android.content.ComponentName;
 import android.content.Intent;
@@ -33,6 +38,8 @@
 import android.os.PatternMatcher;
 import android.os.UserHandle;
 import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
 import android.util.AttributeSet;
 import android.util.Base64;
 import android.util.DisplayMetrics;
@@ -41,12 +48,18 @@
 import android.util.Slog;
 import android.util.TypedValue;
 
-import java.io.BufferedInputStream;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.XmlUtils;
+
+import libcore.io.IoUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
 import java.security.GeneralSecurityException;
 import java.security.KeyFactory;
 import java.security.NoSuchAlgorithmException;
@@ -58,21 +71,19 @@
 import java.security.spec.X509EncodedKeySpec;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.jar.StrictJarFile;
 import java.util.zip.ZipEntry;
 
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.XmlUtils;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-
 /**
  * Package archive parsing
  *
@@ -153,17 +164,21 @@
                     android.os.Build.VERSION_CODES.JELLY_BEAN)
     };
 
+    /**
+     * @deprecated callers should move to explicitly passing around source path.
+     */
+    @Deprecated
     private String mArchiveSourcePath;
+
     private String[] mSeparateProcesses;
     private boolean mOnlyCoreApps;
+    private DisplayMetrics mMetrics;
+
     private static final int SDK_VERSION = Build.VERSION.SDK_INT;
     private static final String[] SDK_CODENAMES = Build.VERSION.ACTIVE_CODENAMES;
 
     private int mParseError = PackageManager.INSTALL_SUCCEEDED;
 
-    private static final Object mSync = new Object();
-    private static WeakReference<byte[]> mReadBuffer;
-
     private static boolean sCompatibilityModeEnabled = true;
     private static final int PARSE_DEFAULT_INSTALL_LOCATION =
             PackageInfo.INSTALL_LOCATION_UNSPECIFIED;
@@ -247,12 +262,9 @@
 
     private static final String TAG = "PackageParser";
 
-    public PackageParser(String archiveSourcePath) {
-        mArchiveSourcePath = archiveSourcePath;
-    }
-
-    public PackageParser(File archiveSource) {
-        this(archiveSource.getAbsolutePath());
+    public PackageParser() {
+        mMetrics = new DisplayMetrics();
+        mMetrics.setToDefaults();
     }
 
     public void setSeparateProcesses(String[] procs) {
@@ -263,6 +275,10 @@
         mOnlyCoreApps = onlyCoreApps;
     }
 
+    public void setDisplayMetrics(DisplayMetrics metrics) {
+        mMetrics = metrics;
+    }
+
     private static final boolean isPackageFilename(File file) {
         return isPackageFilename(file.getName());
     }
@@ -480,23 +496,21 @@
         return pi;
     }
 
-    private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry je,
-            byte[] readBuffer) {
+    private static Certificate[][] loadCertificates(StrictJarFile jarFile, ZipEntry entry)
+            throws PackageParserException {
+        InputStream is = null;
         try {
             // We must read the stream for the JarEntry to retrieve
             // its certificates.
-            InputStream is = new BufferedInputStream(jarFile.getInputStream(je));
-            while (is.read(readBuffer, 0, readBuffer.length) != -1) {
-                // not using
-            }
-            is.close();
-            return je != null ? jarFile.getCertificateChains(je) : null;
-        } catch (IOException e) {
-            Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e);
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Exception reading " + je.getName() + " in " + jarFile, e);
+            is = jarFile.getInputStream(entry);
+            readFullyIgnoringContents(is);
+            return jarFile.getCertificateChains(entry);
+        } catch (IOException | RuntimeException e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed reading " + entry.getName() + " in " + jarFile, e);
+        } finally {
+            IoUtils.closeQuietly(is);
         }
-        return null;
     }
 
     public final static int PARSE_IS_SYSTEM = 1<<0;
@@ -508,67 +522,116 @@
     public final static int PARSE_IS_SYSTEM_DIR = 1<<6;
     public final static int PARSE_IS_PRIVILEGED = 1<<7;
     public final static int PARSE_GET_SIGNATURES = 1<<8;
+    public final static int PARSE_TRUSTED_OVERLAY = 1<<9;
+
+    private static final Comparator<String> sSplitNameComparator = new SplitNameComparator();
 
     /**
-     * Parse all APK files under the given directory as a single package.
+     * Used to sort a set of APKs based on their split names, always placing the
+     * base APK (with {@code null} split name) first.
      */
-    public Package parseSplitPackage(File apkDir, DisplayMetrics metrics, int flags,
-            boolean trustedOverlay) throws PackageParserException {
+    private static class SplitNameComparator implements Comparator<String> {
+        @Override
+        public int compare(String lhs, String rhs) {
+            if (lhs == null) {
+                return -1;
+            } else if (rhs == null) {
+                return 1;
+            } else {
+                return lhs.compareTo(rhs);
+            }
+        }
+    }
+
+    /**
+     * Parse all APKs contained in the given directory, treating them as a
+     * single package. This also performs sanity checking, such as requiring
+     * identical package name and version codes, a single base APK, and unique
+     * split names.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(Package, int)}.
+     */
+    public Package parseSplitPackage(File apkDir, int flags) throws PackageParserException {
         final File[] files = apkDir.listFiles();
         if (ArrayUtils.isEmpty(files)) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                     "No packages found in split");
         }
 
-        File baseFile = null;
+        String packageName = null;
+        int versionCode = 0;
+
+        final ArrayMap<String, File> apks = new ArrayMap<>();
         for (File file : files) {
             if (file.isFile() && isPackageFilename(file)) {
-                ApkLite lite = parseApkLite(file.getAbsolutePath(), 0);
-                if (lite == null) {
-                    throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
-                            "Invalid package file: " + file);
+                final ApkLite lite = parseApkLite(file, 0);
+
+                // Assert that all package names and version codes are
+                // consistent with the first one we encounter.
+                if (packageName == null) {
+                    packageName = lite.packageName;
+                    versionCode = lite.versionCode;
+                } else {
+                    if (!packageName.equals(lite.packageName)) {
+                        throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                                "Inconsistent package " + lite.packageName + " in " + file
+                                + "; expected " + packageName);
+                    }
+                    if (versionCode != lite.versionCode) {
+                        throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                                "Inconsistent version " + lite.versionCode + " in " + file
+                                + "; expected " + versionCode);
+                    }
                 }
 
-                if (TextUtils.isEmpty(lite.splitName)) {
-                    baseFile = file;
-                    break;
+                // Assert that each split is defined only once
+                if (apks.put(lite.splitName, file) != null) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                            "Split name " + lite.splitName
+                            + " defined more than once; most recent was " + file);
                 }
             }
         }
 
+        final File baseFile = apks.remove(null);
         if (baseFile == null) {
-            throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+            throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
                     "Missing base APK in " + apkDir);
         }
 
-        final Package pkg = parseBaseApk(baseFile, metrics, flags, trustedOverlay);
+        // Always apply deterministic ordering based on splitName
+        final int size = apks.size();
+
+        final String[] splitNames = apks.keySet().toArray(new String[size]);
+        Arrays.sort(splitNames, sSplitNameComparator);
+
+        final File[] splitFiles = new File[size];
+        for (int i = 0; i < size; i++) {
+            splitFiles[i] = apks.get(splitNames[i]);
+        }
+
+        final Package pkg = parseBaseApk(baseFile, flags);
         if (pkg == null) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
                     "Failed to parse base APK: " + baseFile);
         }
 
-        for (File file : files) {
-            if (file.isFile() && isPackageFilename(file) && !file.equals(baseFile)) {
-                parseSplitApk(pkg, file, metrics, flags, trustedOverlay);
-            }
-        }
-
-        // Always use a well-defined sort order
-        if (pkg.splitCodePaths != null) {
-            Arrays.sort(pkg.splitCodePaths);
+        for (File splitFile : splitFiles) {
+            parseSplitApk(pkg, splitFile, flags);
         }
 
         return pkg;
     }
 
-    public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags)
-            throws PackageParserException {
-        return parseMonolithicPackage(apkFile, metrics, flags, false);
-    }
-
-    public Package parseMonolithicPackage(File apkFile, DisplayMetrics metrics, int flags,
-            boolean trustedOverlay) throws PackageParserException {
-        final Package pkg = parseBaseApk(apkFile, metrics, flags, trustedOverlay);
+    /**
+     * Parse the given APK file, treating it as as a single monolithic package.
+     * <p>
+     * Note that this <em>does not</em> perform signature verification; that
+     * must be done separately in {@link #collectCertificates(Package, int)}.
+     */
+    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
+        final Package pkg = parseBaseApk(apkFile, flags);
         if (pkg != null) {
             return pkg;
         } else {
@@ -576,13 +639,15 @@
         }
     }
 
-    private Package parseBaseApk(File apkFile, DisplayMetrics metrics, int flags,
-            boolean trustedOverlay) {
+    private Package parseBaseApk(File apkFile, int flags) {
+        final boolean trustedOverlay = (flags & PARSE_TRUSTED_OVERLAY) != 0;
+
         mParseError = PackageManager.INSTALL_SUCCEEDED;
 
+        final String apkPath = apkFile.getAbsolutePath();
         mArchiveSourcePath = apkFile.getAbsolutePath();
         if (!apkFile.isFile()) {
-            Slog.w(TAG, "Skipping dir: " + mArchiveSourcePath);
+            Slog.w(TAG, "Skipping dir: " + apkPath);
             mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
             return null;
         }
@@ -591,14 +656,14 @@
             if ((flags&PARSE_IS_SYSTEM) == 0) {
                 // We expect to have non-.apk files in the system dir,
                 // so don't warn about them.
-                Slog.w(TAG, "Skipping non-package file: " + mArchiveSourcePath);
+                Slog.w(TAG, "Skipping non-package file: " + apkPath);
             }
             mParseError = PackageManager.INSTALL_PARSE_FAILED_NOT_APK;
             return null;
         }
 
         if (DEBUG_JAR)
-            Slog.d(TAG, "Scanning package: " + mArchiveSourcePath);
+            Slog.d(TAG, "Scanning package: " + apkPath);
 
         XmlResourceParser parser = null;
         AssetManager assmgr = null;
@@ -606,19 +671,18 @@
         boolean assetError = true;
         try {
             assmgr = new AssetManager();
-            int cookie = assmgr.addAssetPath(mArchiveSourcePath);
+            int cookie = assmgr.addAssetPath(apkPath);
             if (cookie != 0) {
-                res = new Resources(assmgr, metrics, null);
+                res = new Resources(assmgr, mMetrics, null);
                 assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                         Build.VERSION.RESOURCES_SDK_INT);
                 parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
                 assetError = false;
             } else {
-                Slog.w(TAG, "Failed adding asset path:"+mArchiveSourcePath);
+                Slog.w(TAG, "Failed adding asset path:" + apkPath);
             }
         } catch (Exception e) {
-            Slog.w(TAG, "Unable to read AndroidManifest.xml of "
-                    + mArchiveSourcePath, e);
+            Slog.w(TAG, "Unable to read AndroidManifest.xml of " + apkPath, e);
         }
         if (assetError) {
             if (assmgr != null) assmgr.close();
@@ -641,9 +705,9 @@
             // just means to skip this app so don't make a fuss about it.
             if (!mOnlyCoreApps || mParseError != PackageManager.INSTALL_SUCCEEDED) {
                 if (errorException != null) {
-                    Slog.w(TAG, mArchiveSourcePath, errorException);
+                    Slog.w(TAG, apkPath, errorException);
                 } else {
-                    Slog.w(TAG, mArchiveSourcePath + " (at "
+                    Slog.w(TAG, apkPath + " (at "
                             + parser.getPositionDescription()
                             + "): " + errorText[0]);
                 }
@@ -659,14 +723,16 @@
         parser.close();
         assmgr.close();
 
-        pkg.codePath = mArchiveSourcePath;
+        pkg.codePath = apkPath;
         pkg.mSignatures = null;
 
         return pkg;
     }
 
-    private void parseSplitApk(Package pkg, File apkFile, DisplayMetrics metrics, int flags,
-            boolean trustedOverlay) throws PackageParserException {
+    private void parseSplitApk(Package pkg, File apkFile, int flags) throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
+        mArchiveSourcePath = apkFile.getAbsolutePath();
+
         // TODO: expand split APK parsing
         pkg.splitCodePaths = ArrayUtils.appendElement(String.class, pkg.splitCodePaths,
                 apkFile.getAbsolutePath());
@@ -678,8 +744,11 @@
      * {@code AndroidManifest.xml}, {@code true} is returned.
      */
     public void collectManifestDigest(Package pkg) throws PackageParserException {
+        pkg.manifestDigest = null;
+
+        // TODO: extend to gather digest for split APKs
         try {
-            final StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath);
+            final StrictJarFile jarFile = new StrictJarFile(pkg.codePath);
             try {
                 final ZipEntry je = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
                 if (je != null) {
@@ -688,186 +757,127 @@
             } finally {
                 jarFile.close();
             }
-        } catch (IOException e) {
+        } catch (IOException | RuntimeException e) {
             throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
                     "Failed to collect manifest digest");
         }
     }
 
+    /**
+     * Collect certificates from all the APKs described in the given package,
+     * populating {@link Package#mSignatures}. This also asserts that all APK
+     * contents are signed correctly and consistently.
+     */
     public void collectCertificates(Package pkg, int flags) throws PackageParserException {
-        if (!collectCertificatesInternal(pkg, flags)) {
-            throw new PackageParserException(mParseError, "Failed to collect certificates");
+        pkg.mCertificates = null;
+        pkg.mSignatures = null;
+        pkg.mSigningKeys = null;
+
+        collectCertificates(pkg, new File(pkg.codePath), flags);
+
+        if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
+            for (String splitCodePath : pkg.splitCodePaths) {
+                collectCertificates(pkg, new File(splitCodePath), flags);
+            }
         }
     }
 
-    private boolean collectCertificatesInternal(Package pkg, int flags) {
-        pkg.mSignatures = null;
+    private static void collectCertificates(Package pkg, File apkFile, int flags)
+            throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
 
-        WeakReference<byte[]> readBufferRef;
-        byte[] readBuffer = null;
-        synchronized (mSync) {
-            readBufferRef = mReadBuffer;
-            if (readBufferRef != null) {
-                mReadBuffer = null;
-                readBuffer = readBufferRef.get();
-            }
-            if (readBuffer == null) {
-                readBuffer = new byte[8192];
-                readBufferRef = new WeakReference<byte[]>(readBuffer);
-            }
-        }
-
+        StrictJarFile jarFile = null;
         try {
-            StrictJarFile jarFile = new StrictJarFile(mArchiveSourcePath);
+            jarFile = new StrictJarFile(apkPath);
 
-            Certificate[][] certs = null;
+            // Always verify manifest, regardless of source
+            final ZipEntry manifestEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
+            if (manifestEntry == null) {
+                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_MANIFEST,
+                        "Package " + apkPath + " has no manifest");
+            }
 
-            if ((flags&PARSE_IS_SYSTEM) != 0) {
-                // If this package comes from the system image, then we
-                // can trust it...  we'll just use the AndroidManifest.xml
-                // to retrieve its signatures, not validating all of the
-                // files.
-                ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
-                certs = loadCertificates(jarFile, jarEntry, readBuffer);
-                if (certs == null) {
-                    Slog.e(TAG, "Package " + pkg.packageName
-                            + " has no certificates at entry "
-                            + jarEntry.getName() + "; ignoring!");
-                    jarFile.close();
-                    mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-                    return false;
+            final List<ZipEntry> toVerify = new ArrayList<>();
+            toVerify.add(manifestEntry);
+
+            // If we're parsing an untrusted package, verify all contents
+            if ((flags & PARSE_IS_SYSTEM) == 0) {
+                final Iterator<ZipEntry> i = jarFile.iterator();
+                while (i.hasNext()) {
+                    final ZipEntry entry = i.next();
+
+                    if (entry.isDirectory()) continue;
+                    if (entry.getName().startsWith("META-INF/")) continue;
+                    if (entry.getName().equals(ANDROID_MANIFEST_FILENAME)) continue;
+
+                    toVerify.add(entry);
                 }
-                if (DEBUG_JAR) {
-                    Slog.i(TAG, "File " + mArchiveSourcePath + ": entry=" + jarEntry
-                            + " certs=" + (certs != null ? certs.length : 0));
-                    if (certs != null) {
-                        final int N = certs.length;
-                        for (int i=0; i<N; i++) {
-                            Slog.i(TAG, "  Public key: "
-                                    + certs[i][0].getPublicKey().getEncoded()
-                                    + " " + certs[i][0].getPublicKey());
-                        }
-                    }
+            }
+
+            // Verify that entries are signed consistently with the first entry
+            // we encountered. Note that for splits, certificates may have
+            // already been populated during an earlier parse of a base APK.
+            for (ZipEntry entry : toVerify) {
+                final Certificate[][] entryCerts = loadCertificates(jarFile, entry);
+                if (ArrayUtils.isEmpty(entryCerts)) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_NO_CERTIFICATES,
+                            "Package " + apkPath + " has no certificates at entry "
+                            + entry.getName());
                 }
-            } else {
-                Iterator<ZipEntry> entries = jarFile.iterator();
-                while (entries.hasNext()) {
-                    final ZipEntry je = entries.next();
-                    if (je.isDirectory()) continue;
 
-                    final String name = je.getName();
-
-                    if (name.startsWith("META-INF/"))
-                        continue;
-
-                    if (ANDROID_MANIFEST_FILENAME.equals(name)) {
-                        pkg.manifestDigest =
-                                ManifestDigest.fromInputStream(jarFile.getInputStream(je));
+                if (pkg.mCertificates == null) {
+                    pkg.mCertificates = entryCerts;
+                    pkg.mSignatures = convertToSignatures(entryCerts);
+                    pkg.mSigningKeys = new ArraySet<>();
+                    for (int i = 0; i < entryCerts.length; i++) {
+                        pkg.mSigningKeys.add(entryCerts[i][0].getPublicKey());
                     }
-
-                    final Certificate[][] localCerts = loadCertificates(jarFile, je, readBuffer);
-                    if (DEBUG_JAR) {
-                        Slog.i(TAG, "File " + mArchiveSourcePath + " entry " + je.getName()
-                                + ": certs=" + certs + " ("
-                                + (certs != null ? certs.length : 0) + ")");
-                    }
-
-                    if (localCerts == null) {
-                        Slog.e(TAG, "Package " + pkg.packageName
-                                + " has no certificates at entry "
-                                + je.getName() + "; ignoring!");
-                        jarFile.close();
-                        mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-                        return false;
-                    } else if (certs == null) {
-                        certs = localCerts;
-                    } else {
-                        // Ensure all certificates match.
-                        for (int i=0; i<certs.length; i++) {
-                            boolean found = false;
-                            for (int j=0; j<localCerts.length; j++) {
-                                if (certs[i] != null &&
-                                        certs[i].equals(localCerts[j])) {
-                                    found = true;
-                                    break;
-                                }
-                            }
-                            if (!found || certs.length != localCerts.length) {
-                                Slog.e(TAG, "Package " + pkg.packageName
+                } else {
+                    final boolean certsMatch = (pkg.mCertificates.length == entryCerts.length)
+                            && ArrayUtils.containsAll(pkg.mCertificates, entryCerts)
+                            && ArrayUtils.containsAll(entryCerts, pkg.mCertificates);
+                    if (!certsMatch) {
+                        throw new PackageParserException(
+                                INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES, "Package " + apkPath
                                         + " has mismatched certificates at entry "
-                                        + je.getName() + "; ignoring!");
-                                jarFile.close();
-                                mParseError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
-                                return false;
-                            }
-                        }
+                                        + entry.getName());
                     }
                 }
             }
-            jarFile.close();
-
-            synchronized (mSync) {
-                mReadBuffer = readBufferRef;
-            }
-
-            if (!ArrayUtils.isEmpty(certs)) {
-                pkg.mSignatures = convertToSignatures(certs);
-            } else {
-                Slog.e(TAG, "Package " + pkg.packageName
-                        + " has no certificates; ignoring!");
-                mParseError = PackageManager.INSTALL_PARSE_FAILED_NO_CERTIFICATES;
-                return false;
-            }
-
-            // Add the signing KeySet to the system
-            pkg.mSigningKeys = new HashSet<PublicKey>();
-            for (int i=0; i < certs.length; i++) {
-                pkg.mSigningKeys.add(certs[i][0].getPublicKey());
-            }
-
-        } catch (CertificateEncodingException e) {
-            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
-            return false;
-        } catch (IOException e) {
-            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
-            return false;
-        } catch (SecurityException e) {
-            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING;
-            return false;
-        } catch (RuntimeException e) {
-            Slog.w(TAG, "Exception reading " + mArchiveSourcePath, e);
-            mParseError = PackageManager.INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION;
-            return false;
+        } catch (GeneralSecurityException | IOException | RuntimeException e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
+                    "Failed to collect certificates from " + apkPath, e);
+        } finally {
+            closeQuietly(jarFile);
         }
-
-        return true;
     }
 
     /**
      * Only collect certificates on the manifest; does not validate signatures
      * across remainder of package.
      */
-    private static Signature[] collectCertificates(String packageFilePath) {
+    private static Signature[] collectManifestCertificates(File apkFile)
+            throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
         try {
-            final StrictJarFile jarFile = new StrictJarFile(packageFilePath);
+            final StrictJarFile jarFile = new StrictJarFile(apkPath);
             try {
                 final ZipEntry jarEntry = jarFile.findEntry(ANDROID_MANIFEST_FILENAME);
-                if (jarEntry != null) {
-                    final Certificate[][] certs = loadCertificates(jarFile, jarEntry, null);
-                    return convertToSignatures(certs);
+                if (jarEntry == null) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_MANIFEST_MALFORMED,
+                            "Package " + apkPath + " has no manifest");
                 }
+
+                final Certificate[][] certs = loadCertificates(jarFile, jarEntry);
+                return convertToSignatures(certs);
+
             } finally {
                 jarFile.close();
             }
-        } catch (GeneralSecurityException e) {
-            Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e);
-        } catch (IOException e) {
-            Slog.w(TAG, "Failed to collect certs from " + packageFilePath + ": " + e);
+        } catch (GeneralSecurityException | IOException | RuntimeException e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING,
+                    "Failed to collect certificates from " + apkPath, e);
         }
-        return null;
     }
 
     private static Signature[] convertToSignatures(Certificate[][] certs)
@@ -879,67 +889,55 @@
         return res;
     }
 
-    /*
-     * Utility method that retrieves just the package name and install
-     * location from the apk location at the given file path.
-     * @param packageFilePath file location of the apk
-     * @param flags Special parse flags
-     * @return PackageLite object with package information or null on failure.
+    /**
+     * Utility method that retrieves lightweight details about a single APK
+     * file, including package name, split name, and install location.
+     *
+     * @param apkFile path to a single APK
+     * @param flags optional parse flags, such as {@link #PARSE_GET_SIGNATURES}
      */
-    public static ApkLite parseApkLite(String packageFilePath, int flags) {
+    public static ApkLite parseApkLite(File apkFile, int flags)
+            throws PackageParserException {
+        final String apkPath = apkFile.getAbsolutePath();
+
         AssetManager assmgr = null;
-        final XmlResourceParser parser;
-        final Resources res;
+        XmlResourceParser parser = null;
         try {
             assmgr = new AssetManager();
             assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                     Build.VERSION.RESOURCES_SDK_INT);
 
-            int cookie = assmgr.addAssetPath(packageFilePath);
+            int cookie = assmgr.addAssetPath(apkPath);
             if (cookie == 0) {
-                return null;
+                throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
+                        "Failed to parse " + apkPath);
             }
 
             final DisplayMetrics metrics = new DisplayMetrics();
             metrics.setToDefaults();
-            res = new Resources(assmgr, metrics, null);
+
+            final Resources res = new Resources(assmgr, metrics, null);
             parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
-        } catch (Exception e) {
-            if (assmgr != null) assmgr.close();
-            Slog.w(TAG, "Unable to read AndroidManifest.xml of "
-                    + packageFilePath, e);
-            return null;
-        }
 
-        // Only collect certificates on the manifest; does not validate
-        // signatures across remainder of package.
-        final Signature[] signatures;
-        if ((flags & PARSE_GET_SIGNATURES) != 0) {
-            signatures = collectCertificates(packageFilePath);
-        } else {
-            signatures = null;
-        }
+            // Only collect certificates on the manifest; does not validate
+            // signatures across remainder of package.
+            final Signature[] signatures;
+            if ((flags & PARSE_GET_SIGNATURES) != 0) {
+                signatures = collectManifestCertificates(apkFile);
+            } else {
+                signatures = null;
+            }
 
-        final AttributeSet attrs = parser;
-        final String errors[] = new String[1];
-        ApkLite packageLite = null;
-        try {
-            packageLite = parseApkLite(res, parser, attrs, flags, signatures, errors);
-        } catch (PackageParserException e) {
-            Slog.w(TAG, packageFilePath, e);
-        } catch (IOException e) {
-            Slog.w(TAG, packageFilePath, e);
-        } catch (XmlPullParserException e) {
-            Slog.w(TAG, packageFilePath, e);
+            final AttributeSet attrs = parser;
+            return parseApkLite(res, parser, attrs, flags, signatures);
+
+        } catch (XmlPullParserException | IOException | RuntimeException e) {
+            throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
+                    "Failed to parse " + apkPath, e);
         } finally {
             if (parser != null) parser.close();
             if (assmgr != null) assmgr.close();
         }
-        if (packageLite == null) {
-            Slog.e(TAG, "parsePackageLite error: " + errors[0]);
-            return null;
-        }
-        return packageLite;
     }
 
     private static String validateName(String name, boolean requiresSeparator) {
@@ -995,12 +993,16 @@
             }
         }
 
-        final String splitName = attrs.getAttributeValue(null, "split");
+        String splitName = attrs.getAttributeValue(null, "split");
         if (splitName != null) {
-            final String error = validateName(splitName, true);
-            if (error != null) {
-                throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
-                        "Invalid manifest split: " + error);
+            if (splitName.length() == 0) {
+                splitName = null;
+            } else {
+                final String error = validateName(splitName, true);
+                if (error != null) {
+                    throw new PackageParserException(INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME,
+                            "Invalid manifest split: " + error);
+                }
             }
         }
 
@@ -1009,8 +1011,8 @@
     }
 
     private static ApkLite parseApkLite(Resources res, XmlPullParser parser,
-            AttributeSet attrs, int flags, Signature[] signatures, String[] outError)
-            throws IOException, XmlPullParserException, PackageParserException {
+            AttributeSet attrs, int flags, Signature[] signatures) throws IOException,
+            XmlPullParserException, PackageParserException {
         final Pair<String, String> packageSplit = parsePackageSplitNames(parser, attrs, flags);
 
         int installLocation = PARSE_DEFAULT_INSTALL_LOCATION;
@@ -1043,7 +1045,7 @@
             }
 
             if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) {
-                final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags, outError);
+                final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags);
                 if (verifier != null) {
                     verifiers.add(verifier);
                 }
@@ -1793,7 +1795,7 @@
             }
         }
 
-        owner.mKeySetMapping = new HashMap<String, Set<PublicKey>>();
+        owner.mKeySetMapping = new ArrayMap<String, ArraySet<PublicKey>>();
         for (Map.Entry<PublicKey, Set<String>> e : definedKeySets.entrySet()) {
             PublicKey key = e.getKey();
             Set<String> keySetNames = e.getValue();
@@ -1801,7 +1803,7 @@
                 if (owner.mKeySetMapping.containsKey(alias)) {
                     owner.mKeySetMapping.get(alias).add(key);
                 } else {
-                    Set<PublicKey> keys = new HashSet<PublicKey>();
+                    ArraySet<PublicKey> keys = new ArraySet<PublicKey>();
                     keys.add(key);
                     owner.mKeySetMapping.put(alias, keys);
                 }
@@ -2088,6 +2090,11 @@
                         false)) {
                     ai.flags |= ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
                 }
+                if (sa.getBoolean(
+                        com.android.internal.R.styleable.AndroidManifestApplication_fullBackupOnly,
+                        false)) {
+                    ai.flags |= ApplicationInfo.FLAG_FULL_BACKUP_ONLY;
+                }
             }
         }
 
@@ -2607,10 +2614,9 @@
                     com.android.internal.R.styleable.AndroidManifestActivity_windowSoftInputMode,
                     0);
 
-            if (sa.getBoolean(
-                    com.android.internal.R.styleable.AndroidManifestActivity_persistable, false)) {
-                a.info.flags |= ActivityInfo.FLAG_PERSISTABLE;
-            }
+            a.info.persistableMode = sa.getInteger(
+                    com.android.internal.R.styleable.AndroidManifestActivity_persistableMode,
+                    ActivityInfo.PERSIST_ROOT_ONLY);
 
             if (sa.getBoolean(
                     com.android.internal.R.styleable.AndroidManifestActivity_allowEmbedded,
@@ -2623,6 +2629,12 @@
                     false)) {
                 a.info.flags |= ActivityInfo.FLAG_AUTO_REMOVE_FROM_RECENTS;
             }
+
+            if (sa.getBoolean(
+                    com.android.internal.R.styleable.AndroidManifestActivity_relinquishTaskIdentity,
+                    false)) {
+                a.info.flags |= ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY;
+            }
         } else {
             a.info.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
             a.info.configChanges = 0;
@@ -3428,8 +3440,7 @@
     }
 
     private static VerifierInfo parseVerifier(Resources res, XmlPullParser parser,
-            AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException,
-            IOException {
+            AttributeSet attrs, int flags) {
         final TypedArray sa = res.obtainAttributes(attrs,
                 com.android.internal.R.styleable.AndroidManifestPackageVerifier);
 
@@ -3672,7 +3683,10 @@
         public String packageName;
 
         // TODO: work towards making these paths invariant
+
+        /** Base APK */
         public String codePath;
+        /** Split APKs, ordered by parsed splitName */
         public String[] splitCodePaths;
 
         // For now we only support one application per package.
@@ -3718,7 +3732,8 @@
         public int mSharedUserLabel;
 
         // Signatures that were read from the package.
-        public Signature mSignatures[];
+        public Signature[] mSignatures;
+        public Certificate[][] mCertificates;
 
         // For use by package manager service for quick lookup of
         // preferred up order.
@@ -3780,8 +3795,8 @@
         /**
          * Data used to feed the KeySetManager
          */
-        public Set<PublicKey> mSigningKeys;
-        public Map<String, Set<PublicKey>> mKeySetMapping;
+        public ArraySet<PublicKey> mSigningKeys;
+        public ArrayMap<String, ArraySet<PublicKey>> mKeySetMapping;
 
         public Package(String packageName) {
             this.packageName = packageName;
@@ -3789,6 +3804,15 @@
             applicationInfo.uid = -1;
         }
 
+        public Collection<String> getAllCodePaths() {
+            ArrayList<String> paths = new ArrayList<>();
+            paths.add(codePath);
+            if (!ArrayUtils.isEmpty(splitCodePaths)) {
+                Collections.addAll(paths, splitCodePaths);
+            }
+            return paths;
+        }
+
         public void setPackageName(String newName) {
             packageName = newName;
             applicationInfo.packageName = newName;
@@ -4391,6 +4415,33 @@
         sCompatibilityModeEnabled = compatibilityModeEnabled;
     }
 
+    private static AtomicReference<byte[]> sBuffer = new AtomicReference<byte[]>();
+
+    public static long readFullyIgnoringContents(InputStream in) throws IOException {
+        byte[] buffer = sBuffer.getAndSet(null);
+        if (buffer == null) {
+            buffer = new byte[4096];
+        }
+
+        int n = 0;
+        int count = 0;
+        while ((n = in.read(buffer, 0, buffer.length)) != -1) {
+            count += n;
+        }
+
+        sBuffer.set(buffer);
+        return count;
+    }
+
+    public static void closeQuietly(StrictJarFile jarFile) {
+        if (jarFile != null) {
+            try {
+                jarFile.close();
+            } catch (Exception ignored) {
+            }
+        }
+    }
+
     public static class PackageParserException extends Exception {
         public final int error;
 
@@ -4398,5 +4449,10 @@
             super(detailMessage);
             this.error = error;
         }
+
+        public PackageParserException(int error, String detailMessage, Throwable throwable) {
+            super(detailMessage, throwable);
+            this.error = error;
+        }
     }
 }
diff --git a/core/java/android/content/pm/ResolveInfo.java b/core/java/android/content/pm/ResolveInfo.java
index 1ff41c0..1f9d60c 100644
--- a/core/java/android/content/pm/ResolveInfo.java
+++ b/core/java/android/content/pm/ResolveInfo.java
@@ -19,8 +19,11 @@
 import android.content.ComponentName;
 import android.content.IntentFilter;
 import android.graphics.drawable.Drawable;
+import android.graphics.Bitmap;
+import android.graphics.drawable.BitmapDrawable;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.UserHandle;
 import android.text.TextUtils;
 import android.util.Printer;
 import android.util.Slog;
@@ -127,6 +130,18 @@
     public String resolvePackageName;
 
     /**
+     * If not equal to UserHandle.USER_CURRENT, then the intent will be forwarded to this user.
+     * @hide
+     */
+    public int targetUserId;
+
+    /**
+     * If true, then loadIcon will return the icon of the target user.
+     * @hide
+     */
+    public boolean showTargetUserIcon;
+
+    /**
      * @hide Target comes from system process?
      */
     public boolean system;
@@ -202,6 +217,10 @@
                 return dr;
             }
         }
+        if (showTargetUserIcon) {
+            Bitmap bm = pm.getUserIcon(targetUserId);
+            return new BitmapDrawable(bm);
+        }
         return ci.loadIcon(pm);
     }
     
@@ -215,7 +234,9 @@
     public final int getIconResource() {
         if (icon != 0) return icon;
         final ComponentInfo ci = getComponentInfo();
-        if (ci != null) return ci.getIconResource();
+        if (ci != null && !showTargetUserIcon) {
+            return ci.getIconResource();
+        }
         return 0;
     }
 
@@ -250,6 +271,7 @@
     }
     
     public ResolveInfo() {
+        targetUserId = UserHandle.USER_CURRENT;
     }
 
     public ResolveInfo(ResolveInfo orig) {
@@ -266,6 +288,7 @@
         icon = orig.icon;
         resolvePackageName = orig.resolvePackageName;
         system = orig.system;
+        targetUserId = orig.targetUserId;
     }
 
     public String toString() {
@@ -285,6 +308,13 @@
         }
         sb.append(" m=0x");
         sb.append(Integer.toHexString(match));
+        if (targetUserId != UserHandle.USER_CURRENT) {
+            sb.append(" targetUserId=");
+            sb.append(targetUserId);
+        }
+        if (showTargetUserIcon) {
+            sb.append(" [showTargetUserIcon]");
+        }
         sb.append('}');
         return sb.toString();
     }
@@ -320,6 +350,8 @@
         TextUtils.writeToParcel(nonLocalizedLabel, dest, parcelableFlags);
         dest.writeInt(icon);
         dest.writeString(resolvePackageName);
+        dest.writeInt(targetUserId);
+        dest.writeInt(showTargetUserIcon ? 1 : 0);
         dest.writeInt(system ? 1 : 0);
     }
 
@@ -363,6 +395,8 @@
                 = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
         icon = source.readInt();
         resolvePackageName = source.readString();
+        targetUserId = source.readInt();
+        showTargetUserIcon = source.readInt() != 0;
         system = source.readInt() != 0;
     }
     
@@ -374,6 +408,13 @@
         }
 
         public final int compare(ResolveInfo a, ResolveInfo b) {
+            // We want to put the one targeted to another user at the end of the dialog.
+            if (a.targetUserId != UserHandle.USER_CURRENT) {
+                return 1;
+            }
+            if (b.targetUserId != UserHandle.USER_CURRENT) {
+                return -1;
+            }
             CharSequence  sa = a.loadLabel(mPM);
             if (sa == null) sa = a.activityInfo.name;
             CharSequence  sb = b.loadLabel(mPM);
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
index 20dcf83..d2146ac 100644
--- a/core/java/android/content/res/TypedArray.java
+++ b/core/java/android/content/res/TypedArray.java
@@ -885,9 +885,9 @@
 
     /**
      * Extracts theme attributes from a typed array for later resolution using
-     * {@link Theme#resolveAttributes(int[], int[])}. Removes the entries from
-     * the typed array so that subsequent calls to typed getters will return the
-     * default value without crashing.
+     * {@link android.content.res.Resources.Theme#resolveAttributes(int[], int[])}.
+     * Removes the entries from the typed array so that subsequent calls to typed
+     * getters will return the default value without crashing.
      *
      * @return an array of length {@link #getIndexCount()} populated with theme
      *         attributes, or null if there are no theme attributes in the typed
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 0705e0c..cc8503b 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -167,11 +167,16 @@
     private boolean mOneShot;
     private boolean mWithBuffer;
     private boolean mFaceDetectionRunning = false;
-    private Object mAutoFocusCallbackLock = new Object();
+    private final Object mAutoFocusCallbackLock = new Object();
 
     private static final int NO_ERROR = 0;
     private static final int EACCESS = -13;
     private static final int ENODEV = -19;
+    private static final int EBUSY = -16;
+    private static final int EINVAL = -22;
+    private static final int ENOSYS = -38;
+    private static final int EUSERS = -87;
+    private static final int EOPNOTSUPP = -95;
 
     /**
      * Broadcast Action:  A new picture is taken by the camera, and the entry of
@@ -190,6 +195,22 @@
     public static final String ACTION_NEW_VIDEO = "android.hardware.action.NEW_VIDEO";
 
     /**
+     * Camera HAL device API version 1.0
+     * @hide
+     */
+    public static final int CAMERA_HAL_API_VERSION_1_0 = 0x100;
+
+    /**
+     * A constant meaning the normal camera connect/open will be used.
+     */
+    private static final int CAMERA_HAL_API_VERSION_NORMAL_CONNECT = -2;
+
+    /**
+     * Used to indicate HAL version un-specified.
+     */
+    private static final int CAMERA_HAL_API_VERSION_UNSPECIFIED = -1;
+
+    /**
      * Hardware face detection. It does not use much CPU.
      */
     private static final int CAMERA_FACE_DETECTION_HW = 0;
@@ -331,14 +352,82 @@
         return null;
     }
 
-    Camera(int cameraId) {
-        int err = cameraInit(cameraId);
+    /**
+     * Creates a new Camera object to access a particular hardware camera with
+     * given hal API version. If the same camera is opened by other applications
+     * or the hal API version is not supported by this device, this will throw a
+     * RuntimeException.
+     * <p>
+     * You must call {@link #release()} when you are done using the camera,
+     * otherwise it will remain locked and be unavailable to other applications.
+     * <p>
+     * Your application should only have one Camera object active at a time for
+     * a particular hardware camera.
+     * <p>
+     * Callbacks from other methods are delivered to the event loop of the
+     * thread which called open(). If this thread has no event loop, then
+     * callbacks are delivered to the main application event loop. If there is
+     * no main application event loop, callbacks are not delivered.
+     * <p class="caution">
+     * <b>Caution:</b> On some devices, this method may take a long time to
+     * complete. It is best to call this method from a worker thread (possibly
+     * using {@link android.os.AsyncTask}) to avoid blocking the main
+     * application UI thread.
+     *
+     * @param cameraId The hardware camera to access, between 0 and
+     * {@link #getNumberOfCameras()}-1.
+     * @param halVersion The HAL API version this camera device to be opened as.
+     * @return a new Camera object, connected, locked and ready for use.
+     *
+     * @throws IllegalArgumentException if the {@code halVersion} is invalid
+     *
+     * @throws RuntimeException if opening the camera fails (for example, if the
+     * camera is in use by another process or device policy manager has disabled
+     * the camera).
+     *
+     * @see android.app.admin.DevicePolicyManager#getCameraDisabled(android.content.ComponentName)
+     * @see #CAMERA_HAL_API_VERSION_1_0
+     *
+     * @hide
+     */
+    public static Camera openLegacy(int cameraId, int halVersion) {
+        if (halVersion < CAMERA_HAL_API_VERSION_1_0) {
+            throw new IllegalArgumentException("Invalid HAL version " + halVersion);
+        }
+
+        return new Camera(cameraId, halVersion);
+    }
+
+    /**
+     * Create a legacy camera object.
+     *
+     * @param cameraId The hardware camera to access, between 0 and
+     * {@link #getNumberOfCameras()}-1.
+     * @param halVersion The HAL API version this camera device to be opened as.
+     */
+    private Camera(int cameraId, int halVersion) {
+        int err = cameraInitVersion(cameraId, halVersion);
         if (checkInitErrors(err)) {
             switch(err) {
                 case EACCESS:
                     throw new RuntimeException("Fail to connect to camera service");
                 case ENODEV:
                     throw new RuntimeException("Camera initialization failed");
+                case ENOSYS:
+                    throw new RuntimeException("Camera initialization failed because some methods"
+                            + " are not implemented");
+                case EOPNOTSUPP:
+                    throw new RuntimeException("Camera initialization failed because the hal"
+                            + " version is not supported by this device");
+                case EINVAL:
+                    throw new RuntimeException("Camera initialization failed because the input"
+                            + " arugments are invalid");
+                case EBUSY:
+                    throw new RuntimeException("Camera initialization failed because the camera"
+                            + " device was already opened");
+                case EUSERS:
+                    throw new RuntimeException("Camera initialization failed because the max"
+                            + " number of camera devices were already opened");
                 default:
                     // Should never hit this.
                     throw new RuntimeException("Unknown camera error");
@@ -346,10 +435,7 @@
         }
     }
 
-    /**
-     * @hide
-     */
-    public int cameraInit(int cameraId) {
+    private int cameraInitVersion(int cameraId, int halVersion) {
         mShutterCallback = null;
         mRawImageCallback = null;
         mJpegCallback = null;
@@ -369,9 +455,48 @@
 
         String packageName = ActivityThread.currentPackageName();
 
-        return native_setup(new WeakReference<Camera>(this), cameraId, packageName);
+        return native_setup(new WeakReference<Camera>(this), cameraId, halVersion, packageName);
     }
 
+    private int cameraInitNormal(int cameraId) {
+        return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_NORMAL_CONNECT);
+    }
+
+    /**
+     * Connect to the camera service using #connectLegacy
+     *
+     * <p>
+     * This acts the same as normal except that it will return
+     * the detailed error code if open fails instead of
+     * converting everything into {@code NO_INIT}.</p>
+     *
+     * <p>Intended to use by the camera2 shim only, do <i>not</i> use this for other code.</p>
+     *
+     * @return a detailed errno error code, or {@code NO_ERROR} on success
+     *
+     * @hide
+     */
+    public int cameraInitUnspecified(int cameraId) {
+        return cameraInitVersion(cameraId, CAMERA_HAL_API_VERSION_UNSPECIFIED);
+    }
+
+    /** used by Camera#open, Camera#open(int) */
+    Camera(int cameraId) {
+        int err = cameraInitNormal(cameraId);
+        if (checkInitErrors(err)) {
+            switch(err) {
+                case EACCESS:
+                    throw new RuntimeException("Fail to connect to camera service");
+                case ENODEV:
+                    throw new RuntimeException("Camera initialization failed");
+                default:
+                    // Should never hit this.
+                    throw new RuntimeException("Unknown camera error");
+            }
+        }
+    }
+
+
     /**
      * @hide
      */
@@ -392,11 +517,12 @@
     Camera() {
     }
 
+    @Override
     protected void finalize() {
         release();
     }
 
-    private native final int native_setup(Object camera_this, int cameraId,
+    private native final int native_setup(Object camera_this, int cameraId, int halVersion,
                                            String packageName);
 
     private native final void native_release();
@@ -929,7 +1055,7 @@
 
     private class EventHandler extends Handler
     {
-        private Camera mCamera;
+        private final Camera mCamera;
 
         public EventHandler(Camera c, Looper looper) {
             super(looper);
@@ -2210,6 +2336,7 @@
          * @hide
          * @deprecated
          */
+        @Deprecated
         public void dump() {
             Log.e(TAG, "dump: size=" + mMap.size());
             for (String k : mMap.keySet()) {
diff --git a/core/java/android/hardware/ICameraService.aidl b/core/java/android/hardware/ICameraService.aidl
index 4c50dda..2bc3dd4 100644
--- a/core/java/android/hardware/ICameraService.aidl
+++ b/core/java/android/hardware/ICameraService.aidl
@@ -69,4 +69,16 @@
      * well-formatted in the generated java method.
      */
     int getCameraVendorTagDescriptor(out BinderHolder desc);
+
+    // Writes the camera1 parameters into a single-element array.
+    int getLegacyParameters(int cameraId, out String[] parameters);
+    // Determines if a particular API version is supported; see ICameraService.h for version defines
+    int supportsCameraApi(int cameraId, int apiVersion);
+
+    int connectLegacy(ICameraClient client, int cameraId,
+                    int halVersion,
+                    String clientPackageName,
+                    int clientUid,
+                    // Container for an ICamera object
+                    out BinderHolder device);
 }
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index 4a87680..e2f88eb 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -579,6 +579,7 @@
      * of the lens that can be focused correctly.</p>
      * <p>If the lens is fixed-focus, this should be
      * 0.</p>
+     * <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
      */
     public static final Key<Float> LENS_INFO_MINIMUM_FOCUS_DISTANCE =
             new Key<Float>("android.lens.info.minimumFocusDistance", float.class);
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 0901562..9a3d806 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -19,8 +19,10 @@
 import android.content.Context;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceListener;
+import android.hardware.CameraInfo;
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.legacy.CameraDeviceUserShim;
+import android.hardware.camera2.legacy.LegacyMetadataMapper;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.hardware.camera2.utils.BinderHolder;
@@ -57,6 +59,10 @@
     private static final String CAMERA_SERVICE_BINDER_NAME = "media.camera";
     private static final int USE_CALLING_UID = -1;
 
+    @SuppressWarnings("unused")
+    private static final int API_VERSION_1 = 1;
+    private static final int API_VERSION_2 = 2;
+
     private final ICameraService mCameraService;
     private ArrayList<String> mDeviceIdList;
 
@@ -142,6 +148,9 @@
 
         synchronized (mLock) {
             mListenerMap.put(listener, handler);
+
+            // TODO: fire the current oldest known state when adding a new listener
+            //    (must be done while holding lock)
         }
     }
 
@@ -185,16 +194,46 @@
             }
         }
 
-        CameraMetadataNative info = new CameraMetadataNative();
-        try {
-            mCameraService.getCameraCharacteristics(Integer.valueOf(cameraId), info);
-        } catch(CameraRuntimeException e) {
-            throw e.asChecked();
-        } catch(RemoteException e) {
-            // impossible
-            return null;
+        int id = Integer.valueOf(cameraId);
+
+        /*
+         * Get the camera characteristics from the camera service directly if it supports it,
+         * otherwise get them from the legacy shim instead.
+         */
+
+        if (!supportsCamera2Api(cameraId)) {
+            // Legacy backwards compatibility path; build static info from the camera parameters
+            String[] outParameters = new String[1];
+            try {
+                mCameraService.getLegacyParameters(id, /*out*/outParameters);
+                String parameters = outParameters[0];
+
+                CameraInfo info = new CameraInfo();
+                mCameraService.getCameraInfo(id, /*out*/info);
+
+                return LegacyMetadataMapper.createCharacteristics(parameters, info);
+            } catch (RemoteException e) {
+                // Impossible
+                return null;
+            } catch (CameraRuntimeException e) {
+                throw e.asChecked();
+            }
+
+        } else {
+            // Normal path: Get the camera characteristics directly from the camera service
+            CameraMetadataNative info = new CameraMetadataNative();
+
+            try {
+                mCameraService.getCameraCharacteristics(id, info);
+            } catch(CameraRuntimeException e) {
+                throw e.asChecked();
+            } catch(RemoteException e) {
+                // impossible
+                return null;
+            }
+
+            return new CameraCharacteristics(info);
         }
-        return new CameraCharacteristics(info);
     }
 
     /**
@@ -239,14 +278,19 @@
                 ICameraDeviceCallbacks callbacks = deviceImpl.getCallbacks();
                 int id = Integer.parseInt(cameraId);
                 try {
-                    mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
-                            USE_CALLING_UID, holder);
-                    cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
-                } catch (CameraRuntimeException e) {
-                    if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+                    if (supportsCamera2Api(cameraId)) {
+                        // Use cameraservice's cameradeviceclient implementation for HAL3.2+ devices
+                        mCameraService.connectDevice(callbacks, id, mContext.getPackageName(),
+                                USE_CALLING_UID, holder);
+                        cameraUser = ICameraDeviceUser.Stub.asInterface(holder.getBinder());
+                    } else {
                         // Use legacy camera implementation for HAL1 devices
                         Log.i(TAG, "Using legacy camera HAL.");
                         cameraUser = CameraDeviceUserShim.connectBinderShim(callbacks, id);
+                    }
+                } catch (CameraRuntimeException e) {
+                    if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+                        throw new AssertionError("Should've gone down the shim path");
                     } else if (e.getReason() == CameraAccessException.CAMERA_IN_USE ||
                             e.getReason() == CameraAccessException.MAX_CAMERAS_IN_USE ||
                             e.getReason() == CameraAccessException.CAMERA_DISABLED ||
@@ -456,6 +500,53 @@
         }
     }
 
+    /**
+     * Queries the camera service if it supports the camera2 api directly, or needs a shim.
+     *
+     * @param cameraId a non-{@code null} camera identifier
+     * @return {@code false} if the legacy shim needs to be used, {@code true} otherwise.
+     */
+    private boolean supportsCamera2Api(String cameraId) {
+        return supportsCameraApi(cameraId, API_VERSION_2);
+    }
+
+    /**
+     * Queries the camera service if it supports a camera api directly, or needs a shim.
+     *
+     * @param cameraId a non-{@code null} camera identifier
+     * @param apiVersion the version, i.e. {@code API_VERSION_1} or {@code API_VERSION_2}
+     * @return {@code true} if connecting will work for that device version.
+     */
+    private boolean supportsCameraApi(String cameraId, int apiVersion) {
+        int id = Integer.parseInt(cameraId);
+
+        /*
+         * Possible return values:
+         * - NO_ERROR => Camera2 API is supported
+         * - CAMERA_DEPRECATED_HAL => Camera2 API is *not* supported (thrown as an exception)
+         *
+         * Anything else is an unexpected error we don't want to recover from.
+         */
+
+        try {
+            int res = mCameraService.supportsCameraApi(id, apiVersion);
+
+            if (res != CameraBinderDecorator.NO_ERROR) {
+                throw new AssertionError("Unexpected value " + res);
+            }
+
+            return true;
+        } catch (CameraRuntimeException e) {
+            if (e.getReason() == CameraAccessException.CAMERA_DEPRECATED_HAL) {
+                return false;
+            } else {
+                throw e;
+            }
+        } catch (RemoteException e) {
+            throw new AssertionError("Camera service unreachable", e);
+        }
+    }
+
     // TODO: this class needs unit tests
     // TODO: extract class into top level
     private class CameraServiceListener extends ICameraServiceListener.Stub {
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 5a02435..dad1854 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -248,7 +248,6 @@
      * <li>Manual frame duration control<ul>
      * <li>{@link CaptureRequest#SENSOR_FRAME_DURATION android.sensor.frameDuration}</li>
      * <li>{@link CameraCharacteristics#SENSOR_INFO_MAX_FRAME_DURATION android.sensor.info.maxFrameDuration}</li>
-     * <li>{@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap}</li>
      * </ul>
      * </li>
      * <li>Manual exposure control<ul>
@@ -279,6 +278,9 @@
      * result.</p>
      * <p>A given camera device may also support additional manual sensor controls,
      * but this capability only covers the above list of controls.</p>
+     * <p>If this is supported, {@link CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP android.scaler.streamConfigurationMap} will
+     * additionally return a min frame duration that is greater than
+     * zero for each supported size-format combination.</p>
      *
      * @see CaptureRequest#BLACK_LEVEL_LOCK
      * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 2cfb611..5bc59dc 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -18,6 +18,7 @@
 
 import android.hardware.camera2.CameraCharacteristics.Key;
 import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.utils.HashCodeHelpers;
 import android.hardware.camera2.utils.TypeReference;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -280,7 +281,7 @@
 
     @Override
     public int hashCode() {
-        return mSettings.hashCode();
+        return HashCodeHelpers.hashCode(mSettings, mSurfaceSet, mUserTag);
     }
 
     public static final Parcelable.Creator<CaptureRequest> CREATOR =
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index d9f3af4..2e59eee 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -28,6 +28,8 @@
 import android.hardware.camera2.TotalCaptureResult;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
+import android.hardware.camera2.utils.CloseableLock;
+import android.hardware.camera2.utils.CloseableLock.ScopedLock;
 import android.hardware.camera2.utils.LongParcelable;
 import android.os.Handler;
 import android.os.IBinder;
@@ -57,13 +59,14 @@
     // TODO: guard every function with if (!mRemoteDevice) check (if it was closed)
     private ICameraDeviceUser mRemoteDevice;
 
-    private final Object mLock = new Object();
+    private final CloseableLock mCloseLock;
     private final CameraDeviceCallbacks mCallbacks = new CameraDeviceCallbacks();
 
     private final StateListener mDeviceListener;
     private volatile StateListener mSessionStateListener;
     private final Handler mDeviceHandler;
 
+    private volatile boolean mClosing = false;
     private boolean mInError = false;
     private boolean mIdle = true;
 
@@ -100,7 +103,9 @@
     private final Runnable mCallOnOpened = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onOpened(CameraDeviceImpl.this);
@@ -113,7 +118,9 @@
     private final Runnable mCallOnUnconfigured = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onUnconfigured(CameraDeviceImpl.this);
@@ -126,7 +133,9 @@
     private final Runnable mCallOnActive = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onActive(CameraDeviceImpl.this);
@@ -139,7 +148,9 @@
     private final Runnable mCallOnBusy = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onBusy(CameraDeviceImpl.this);
@@ -150,20 +161,29 @@
     };
 
     private final Runnable mCallOnClosed = new Runnable() {
+        private boolean mClosedOnce = false;
+
         @Override
         public void run() {
+            if (mClosedOnce) {
+                throw new AssertionError("Don't post #onClosed more than once");
+            }
+
             StateListener sessionListener = mSessionStateListener;
             if (sessionListener != null) {
                 sessionListener.onClosed(CameraDeviceImpl.this);
             }
             mDeviceListener.onClosed(CameraDeviceImpl.this);
+            mClosedOnce = true;
         }
     };
 
     private final Runnable mCallOnIdle = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onIdle(CameraDeviceImpl.this);
@@ -176,7 +196,9 @@
     private final Runnable mCallOnDisconnected = new Runnable() {
         @Override
         public void run() {
-            if (!CameraDeviceImpl.this.isClosed()) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 StateListener sessionListener = mSessionStateListener;
                 if (sessionListener != null) {
                     sessionListener.onDisconnected(CameraDeviceImpl.this);
@@ -195,6 +217,7 @@
         mDeviceListener = listener;
         mDeviceHandler = handler;
         mCharacteristics = characteristics;
+        mCloseLock = new CloseableLock(/*name*/"CD-" + mCameraId);
 
         final int MAX_TAG_LEN = 23;
         String tag = String.format("CameraDevice-JV-%s", mCameraId);
@@ -210,8 +233,8 @@
     }
 
     public void setRemoteDevice(ICameraDeviceUser remoteDevice) {
+        try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
         // TODO: Move from decorator to direct binder-mediated exceptions
-        synchronized(mLock) {
             // If setRemoteFailure already called, do nothing
             if (mInError) return;
 
@@ -254,7 +277,9 @@
         }
         final int code = failureCode;
         final boolean isError = failureIsError;
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+            if (scopedLock == null) return; // Camera already closed, can't go to error state
+
             mInError = true;
             mDeviceHandler.post(new Runnable() {
                 @Override
@@ -280,7 +305,7 @@
         if (outputs == null) {
             outputs = new ArrayList<Surface>();
         }
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
 
             HashSet<Surface> addSet = new HashSet<Surface>(outputs);    // Streams to create
@@ -344,7 +369,7 @@
     public void createCaptureSession(List<Surface> outputs,
             CameraCaptureSession.StateListener listener, Handler handler)
             throws CameraAccessException {
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             if (DEBUG) {
                 Log.d(TAG, "createCaptureSession");
             }
@@ -389,7 +414,7 @@
     @Override
     public CaptureRequest.Builder createCaptureRequest(int templateType)
             throws CameraAccessException {
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
 
             CameraMetadataNative templatedRequest = new CameraMetadataNative();
@@ -435,7 +460,7 @@
      * starting and stopping repeating request and flushing.
      *
      * <p>If lastFrameNumber is NO_FRAMES_CAPTURED, it means that the request was never
-     * sent to HAL. Then onCaptureSequenceCompleted is immediately triggered.
+     * sent to HAL. Then onCaptureSequenceAborted is immediately triggered.
      * If lastFrameNumber is non-negative, then the requestId and lastFrameNumber pair
      * is added to the list mFrameNumberRequestPairs.</p>
      *
@@ -446,7 +471,7 @@
     private void checkEarlyTriggerSequenceComplete(
             final int requestId, final long lastFrameNumber) {
         // lastFrameNumber being equal to NO_FRAMES_CAPTURED means that the request
-        // was never sent to HAL. Should trigger onCaptureSequenceCompleted immediately.
+        // was never sent to HAL. Should trigger onCaptureSequenceAborted immediately.
         if (lastFrameNumber == CaptureListener.NO_FRAMES_CAPTURED) {
             final CaptureListenerHolder holder;
             int index = mCaptureListenerMap.indexOfKey(requestId);
@@ -463,7 +488,7 @@
 
             if (holder != null) {
                 if (DEBUG) {
-                    Log.v(TAG, "immediately trigger onCaptureSequenceCompleted because"
+                    Log.v(TAG, "immediately trigger onCaptureSequenceAborted because"
                             + " request did not reach HAL");
                 }
 
@@ -480,10 +505,9 @@
                                     || lastFrameNumber > Integer.MAX_VALUE) {
                                 throw new AssertionError(lastFrameNumber + " cannot be cast to int");
                             }
-                            holder.getListener().onCaptureSequenceCompleted(
+                            holder.getListener().onCaptureSequenceAborted(
                                     CameraDeviceImpl.this,
-                                    requestId,
-                                    lastFrameNumber);
+                                    requestId);
                         }
                     }
                 };
@@ -509,7 +533,7 @@
             handler = checkHandler(handler);
         }
 
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
             int requestId;
 
@@ -581,7 +605,7 @@
     @Override
     public void stopRepeating() throws CameraAccessException {
 
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
             if (mRepeatingRequestId != REQUEST_ID_NONE) {
 
@@ -612,7 +636,7 @@
 
     private void waitUntilIdle() throws CameraAccessException {
 
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
             if (mRepeatingRequestId != REQUEST_ID_NONE) {
                 throw new IllegalStateException("Active repeating request ongoing");
@@ -633,7 +657,7 @@
 
     @Override
     public void flush() throws CameraAccessException {
-        synchronized (mLock) {
+        try (ScopedLock scopedLock = mCloseLock.acquireExclusiveLock()) {
             checkIfCameraClosedOrInError();
 
             mDeviceHandler.post(mCallOnBusy);
@@ -656,8 +680,15 @@
 
     @Override
     public void close() {
-        synchronized (mLock) {
+        mClosing = true;
+        // Acquire exclusive lock, close, release (idempotent)
+        mCloseLock.close();
 
+        /*
+         *  The rest of this is safe, since no other methods will be able to execute
+         *  (they will throw ISE instead; the callbacks will get dropped)
+         */
+        {
             try {
                 if (mRemoteDevice != null) {
                     mRemoteDevice.disconnect();
@@ -805,7 +836,12 @@
                 // remove request from mCaptureListenerMap
                 final int requestId = frameNumberRequestPair.getValue();
                 final CaptureListenerHolder holder;
-                synchronized (mLock) {
+                try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                    if (scopedLock == null) {
+                        Log.w(TAG, "Camera closed while checking sequences");
+                        return;
+                    }
+
                     int index = mCaptureListenerMap.indexOfKey(requestId);
                     holder = (index >= 0) ? mCaptureListenerMap.valueAt(index)
                             : null;
@@ -890,9 +926,12 @@
         @Override
         public void onCameraError(final int errorCode, CaptureResultExtras resultExtras) {
             Runnable r = null;
-            if (isClosed()) return;
 
-            synchronized(mLock) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) {
+                    return; // Camera already closed
+                }
+
                 mInError = true;
                 switch (errorCode) {
                     case ERROR_CAMERA_DISCONNECTED:
@@ -914,25 +953,24 @@
                         break;
                 }
                 CameraDeviceImpl.this.mDeviceHandler.post(r);
-            }
 
-            // Fire onCaptureSequenceCompleted
-            if (DEBUG) {
-                Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
+                // Fire onCaptureSequenceCompleted
+                if (DEBUG) {
+                    Log.v(TAG, String.format("got error frame %d", resultExtras.getFrameNumber()));
+                }
+                mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
+                checkAndFireSequenceComplete();
             }
-            mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/true);
-            checkAndFireSequenceComplete();
-
         }
 
         @Override
         public void onCameraIdle() {
-            if (isClosed()) return;
-
             if (DEBUG) {
                 Log.d(TAG, "Camera now idle");
             }
-            synchronized (mLock) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
                 if (!CameraDeviceImpl.this.mIdle) {
                     CameraDeviceImpl.this.mDeviceHandler.post(mCallOnIdle);
                 }
@@ -948,30 +986,33 @@
             }
             final CaptureListenerHolder holder;
 
-            // Get the listener for this frame ID, if there is one
-            synchronized (mLock) {
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
+
+                // Get the listener for this frame ID, if there is one
                 holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
-            }
 
-            if (holder == null) {
-                return;
-            }
+                if (holder == null) {
+                    return;
+                }
 
-            if (isClosed()) return;
+                if (isClosed()) return;
 
-            // Dispatch capture start notice
-            holder.getHandler().post(
-                new Runnable() {
-                    @Override
-                    public void run() {
-                        if (!CameraDeviceImpl.this.isClosed()) {
-                            holder.getListener().onCaptureStarted(
-                                CameraDeviceImpl.this,
-                                holder.getRequest(resultExtras.getSubsequenceId()),
-                                timestamp);
+                // Dispatch capture start notice
+                holder.getHandler().post(
+                    new Runnable() {
+                        @Override
+                        public void run() {
+                            if (!CameraDeviceImpl.this.isClosed()) {
+                                holder.getListener().onCaptureStarted(
+                                    CameraDeviceImpl.this,
+                                    holder.getRequest(resultExtras.getSubsequenceId()),
+                                    timestamp);
+                            }
                         }
-                    }
-                });
+                    });
+
+            }
         }
 
         @Override
@@ -984,88 +1025,91 @@
                         + requestId);
             }
 
+            try (ScopedLock scopedLock = mCloseLock.acquireLock()) {
+                if (scopedLock == null) return; // Camera already closed
 
-            // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
-            result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
-                    getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
+                // TODO: Handle CameraCharacteristics access from CaptureResult correctly.
+                result.set(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE,
+                        getCharacteristics().get(CameraCharacteristics.LENS_INFO_SHADING_MAP_SIZE));
 
-            final CaptureListenerHolder holder;
-            synchronized (mLock) {
-                holder = CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
-            }
+                final CaptureListenerHolder holder =
+                        CameraDeviceImpl.this.mCaptureListenerMap.get(requestId);
 
-            Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
-            boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
+                Boolean quirkPartial = result.get(CaptureResult.QUIRKS_PARTIAL_RESULT);
+                boolean quirkIsPartialResult = (quirkPartial != null && quirkPartial);
 
-            // Update tracker (increment counter) when it's not a partial result.
-            if (!quirkIsPartialResult) {
-                mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(), /*error*/false);
-            }
-
-            // Check if we have a listener for this
-            if (holder == null) {
-                if (DEBUG) {
-                    Log.d(TAG,
-                            "holder is null, early return at frame "
-                                    + resultExtras.getFrameNumber());
+                // Update tracker (increment counter) when it's not a partial result.
+                if (!quirkIsPartialResult) {
+                    mFrameNumberTracker.updateTracker(resultExtras.getFrameNumber(),
+                            /*error*/false);
                 }
-                return;
-            }
 
-            if (isClosed()) {
-                if (DEBUG) {
-                    Log.d(TAG,
-                            "camera is closed, early return at frame "
-                                    + resultExtras.getFrameNumber());
+                // Check if we have a listener for this
+                if (holder == null) {
+                    if (DEBUG) {
+                        Log.d(TAG,
+                                "holder is null, early return at frame "
+                                        + resultExtras.getFrameNumber());
+                    }
+                    return;
                 }
-                return;
-            }
 
-            final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
-
-
-            Runnable resultDispatch = null;
-
-            // Either send a partial result or the final capture completed result
-            if (quirkIsPartialResult) {
-                final CaptureResult resultAsCapture =
-                        new CaptureResult(result, request, requestId);
-
-                // Partial result
-                resultDispatch = new Runnable() {
-                    @Override
-                    public void run() {
-                        if (!CameraDeviceImpl.this.isClosed()){
-                            holder.getListener().onCapturePartial(
-                                CameraDeviceImpl.this,
-                                request,
-                                resultAsCapture);
-                        }
+                if (isClosed()) {
+                    if (DEBUG) {
+                        Log.d(TAG,
+                                "camera is closed, early return at frame "
+                                        + resultExtras.getFrameNumber());
                     }
-                };
-            } else {
-                final TotalCaptureResult resultAsCapture =
-                        new TotalCaptureResult(result, request, requestId);
+                    return;
+                }
 
-                // Final capture result
-                resultDispatch = new Runnable() {
-                    @Override
-                    public void run() {
-                        if (!CameraDeviceImpl.this.isClosed()){
-                            holder.getListener().onCaptureCompleted(
-                                CameraDeviceImpl.this,
-                                request,
-                                resultAsCapture);
+                final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
+
+
+                Runnable resultDispatch = null;
+
+                // Either send a partial result or the final capture completed result
+                if (quirkIsPartialResult) {
+                    final CaptureResult resultAsCapture =
+                            new CaptureResult(result, request, requestId);
+
+                    // Partial result
+                    resultDispatch = new Runnable() {
+                        @Override
+                        public void run() {
+                            if (!CameraDeviceImpl.this.isClosed()){
+                                holder.getListener().onCapturePartial(
+                                    CameraDeviceImpl.this,
+                                    request,
+                                    resultAsCapture);
+                            }
                         }
-                    }
-                };
-            }
+                    };
+                } else {
+                    final TotalCaptureResult resultAsCapture =
+                            new TotalCaptureResult(result, request, requestId);
 
-            holder.getHandler().post(resultDispatch);
+                    // Final capture result
+                    resultDispatch = new Runnable() {
+                        @Override
+                        public void run() {
+                            if (!CameraDeviceImpl.this.isClosed()){
+                                holder.getListener().onCaptureCompleted(
+                                    CameraDeviceImpl.this,
+                                    request,
+                                    resultAsCapture);
+                            }
+                        }
+                    };
+                }
 
-            // Fire onCaptureSequenceCompleted
-            if (!quirkIsPartialResult) {
-                checkAndFireSequenceComplete();
+                holder.getHandler().post(resultDispatch);
+
+                // Fire onCaptureSequenceCompleted
+                if (!quirkIsPartialResult) {
+                    checkAndFireSequenceComplete();
+                }
+
             }
         }
 
@@ -1101,10 +1145,9 @@
         }
     }
 
+    /** Whether the camera device has started to close (may not yet have finished) */
     private boolean isClosed() {
-        synchronized(mLock) {
-            return (mRemoteDevice == null);
-        }
+        return mClosing;
     }
 
     private CameraCharacteristics getCharacteristics() {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
index 22ff9c6..ab7e844 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceState.java
@@ -34,6 +34,7 @@
  *      <li>{@code CONFIGURING -> IDLE}</li>
  *      <li>{@code IDLE -> CONFIGURING}</li>
  *      <li>{@code IDLE -> CAPTURING}</li>
+ *      <li>{@code IDLE -> IDLE}</li>
  *      <li>{@code CAPTURING -> IDLE}</li>
  *      <li>{@code ANY -> ERROR}</li>
  * </ul>
@@ -216,12 +217,17 @@
                 mCurrentState = STATE_CONFIGURING;
                 break;
             case STATE_IDLE:
+                if (mCurrentState == STATE_IDLE) {
+                    break;
+                }
+
                 if (mCurrentState != STATE_CONFIGURING && mCurrentState != STATE_CAPTURING) {
                     Log.e(TAG, "Cannot call idle while in state: " + mCurrentState);
                     mCurrentError = CameraBinderDecorator.INVALID_OPERATION;
                     doStateTransition(STATE_ERROR);
                     break;
                 }
+
                 if (mCurrentState != STATE_IDLE && mCurrentHandler != null &&
                         mCurrentListener != null) {
                     mCurrentHandler.post(new Runnable() {
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 54d9c3c..03dd354 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -17,7 +17,9 @@
 package android.hardware.camera2.legacy;
 
 import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
 import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCharacteristics;
 import android.hardware.camera2.CaptureRequest;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
@@ -25,7 +27,9 @@
 import android.hardware.camera2.impl.CameraMetadataNative;
 import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
+import android.os.ConditionVariable;
 import android.os.IBinder;
+import android.os.Looper;
 import android.os.RemoteException;
 import android.util.Log;
 import android.util.SparseArray;
@@ -33,7 +37,6 @@
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Compatibility implementation of the Camera2 API binder interface.
@@ -53,6 +56,7 @@
     private static final String TAG = "CameraDeviceUserShim";
 
     private static final boolean DEBUG = Log.isLoggable(LegacyCameraDevice.DEBUG_PROP, Log.DEBUG);
+    private static final int OPEN_CAMERA_TIMEOUT_MS = 5000; // 5 sec (same as api1 cts timeout)
 
     private final LegacyCameraDevice mLegacyDevice;
 
@@ -60,31 +64,143 @@
     private int mSurfaceIdCounter;
     private boolean mConfiguring;
     private final SparseArray<Surface> mSurfaces;
+    private final CameraCharacteristics mCameraCharacteristics;
+    private final CameraLooper mCameraInit;
 
-    protected CameraDeviceUserShim(int cameraId, LegacyCameraDevice legacyCamera) {
+    protected CameraDeviceUserShim(int cameraId, LegacyCameraDevice legacyCamera,
+            CameraCharacteristics characteristics, CameraLooper cameraInit) {
         mLegacyDevice = legacyCamera;
         mConfiguring = false;
         mSurfaces = new SparseArray<Surface>();
+        mCameraCharacteristics = characteristics;
+        mCameraInit = cameraInit;
 
         mSurfaceIdCounter = 0;
     }
 
+    /**
+     * Create a separate looper/thread for the camera to run on; open the camera.
+     *
+     * <p>Since the camera automatically latches on to the current thread's looper,
+     * it's important that we have our own thread with our own looper to guarantee
+     * that the camera callbacks get correctly posted to our own thread.</p>
+     */
+    private static class CameraLooper implements Runnable, AutoCloseable {
+        private final int mCameraId;
+        private Looper mLooper;
+        private volatile int mInitErrors;
+        private final Camera mCamera = Camera.openUninitialized();
+        private final ConditionVariable mStartDone = new ConditionVariable();
+        private final Thread mThread;
+
+        /**
+         * Spin up a new thread, immediately open the camera in the background.
+         *
+         * <p>Use {@link #waitForOpen} to block until the camera is finished opening.</p>
+         *
+         * @param cameraId numeric camera Id
+         *
+         * @see #waitForOpen
+         */
+        public CameraLooper(int cameraId) {
+            mCameraId = cameraId;
+
+            mThread = new Thread(this);
+            mThread.start();
+        }
+
+        public Camera getCamera() {
+            return mCamera;
+        }
+
+        @Override
+        public void run() {
+            // Set up a looper to be used by camera.
+            Looper.prepare();
+
+            // Save the looper so that we can terminate this thread
+            // after we are done with it.
+            mLooper = Looper.myLooper();
+            mInitErrors = mCamera.cameraInitUnspecified(mCameraId);
+
+            mStartDone.open();
+            Looper.loop();  // Blocks forever until #close is called.
+        }
+
+        /**
+         * Quit the looper safely; then join until the thread shuts down.
+         */
+        @Override
+        public void close() {
+            if (mLooper == null) {
+                return;
+            }
+
+            mLooper.quitSafely();
+            try {
+                mThread.join();
+            } catch (InterruptedException e) {
+                throw new AssertionError(e);
+            }
+
+            mLooper = null;
+        }
+
+        /**
+         * Block until the camera opens; then return its initialization error code (if any).
+         *
+         * @param timeoutMs timeout in milliseconds
+         *
+         * @return int error code
+         *
+         * @throws CameraRuntimeException if the camera open times out with ({@code CAMERA_ERROR})
+         */
+        public int waitForOpen(int timeoutMs) {
+            // Block until the camera is open asynchronously
+            if (!mStartDone.block(timeoutMs)) {
+                Log.e(TAG, "waitForOpen - Camera failed to open after timeout of "
+                        + OPEN_CAMERA_TIMEOUT_MS + " ms");
+                try {
+                    mCamera.release();
+                } catch (RuntimeException e) {
+                    Log.e(TAG, "connectBinderShim - Failed to release camera after timeout ", e);
+                }
+
+                throw new CameraRuntimeException(CameraAccessException.CAMERA_ERROR);
+            }
+
+            return mInitErrors;
+        }
+    }
+
     public static CameraDeviceUserShim connectBinderShim(ICameraDeviceCallbacks callbacks,
                                                          int cameraId) {
         if (DEBUG) {
             Log.d(TAG, "Opening shim Camera device");
         }
-        // TODO: Move open/init into LegacyCameraDevice thread when API is switched to async.
-        Camera legacyCamera = Camera.openUninitialized();
-        int initErrors = legacyCamera.cameraInit(cameraId);
+
+        /*
+         * Put the camera open on a separate thread with its own looper; otherwise
+         * if the main thread is used then the callbacks might never get delivered
+         * (e.g. in CTS which run its own default looper only after tests)
+         */
+
+        CameraLooper init = new CameraLooper(cameraId);
+
+        // TODO: Make this async instead of blocking
+        int initErrors = init.waitForOpen(OPEN_CAMERA_TIMEOUT_MS);
+        Camera legacyCamera = init.getCamera();
+
         // Check errors old HAL initialization
-        if (Camera.checkInitErrors(initErrors)) {
-            // TODO: Map over old camera error codes.  This likely involves improving the error
-            // reporting in the HAL1 connect path.
-            throw new CameraRuntimeException(CameraAccessException.CAMERA_DISCONNECTED);
-        }
+        CameraBinderDecorator.throwOnError(initErrors);
+
+        CameraInfo info = new CameraInfo();
+        Camera.getCameraInfo(cameraId, info);
+
+        CameraCharacteristics characteristics =
+                LegacyMetadataMapper.createCharacteristics(legacyCamera.getParameters(), info);
         LegacyCameraDevice device = new LegacyCameraDevice(cameraId, legacyCamera, callbacks);
-        return new CameraDeviceUserShim(cameraId, device);
+        return new CameraDeviceUserShim(cameraId, device, characteristics, init);
     }
 
     @Override
@@ -92,7 +208,12 @@
         if (DEBUG) {
             Log.d(TAG, "disconnect called.");
         }
-        mLegacyDevice.close();
+
+        try {
+            mLegacyDevice.close();
+        } finally {
+            mCameraInit.close();
+        }
     }
 
     @Override
@@ -169,7 +290,7 @@
             }
             int numSurfaces = mSurfaces.size();
             if (numSurfaces > 0) {
-                surfaces = new ArrayList<Surface>();
+                surfaces = new ArrayList<>();
                 for (int i = 0; i < numSurfaces; ++i) {
                     surfaces.add(mSurfaces.valueAt(i));
                 }
@@ -220,8 +341,17 @@
         if (DEBUG) {
             Log.d(TAG, "createDefaultRequest called.");
         }
-        // TODO: implement createDefaultRequest.
-        Log.e(TAG, "createDefaultRequest unimplemented.");
+
+        CameraMetadataNative template;
+        try {
+            template =
+                    LegacyMetadataMapper.createRequestTemplate(mCameraCharacteristics, templateId);
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "createDefaultRequest - invalid templateId specified");
+            return CameraBinderDecorator.BAD_VALUE;
+        }
+
+        request.swap(template);
         return CameraBinderDecorator.NO_ERROR;
     }
 
diff --git a/core/java/android/hardware/camera2/legacy/GLThreadManager.java b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
index 3fd2309..5d44fd2 100644
--- a/core/java/android/hardware/camera2/legacy/GLThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/GLThreadManager.java
@@ -100,6 +100,7 @@
                     break;
                 case MSG_ALLOW_FRAMES:
                     mDroppingFrames = false;
+                    break;
                 default:
                     Log.e(TAG, "Unhandled message " + msg.what + " on GLThread.");
                     break;
@@ -148,6 +149,12 @@
         Handler handler = mGLHandlerThread.getHandler();
         handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
         mGLHandlerThread.quitSafely();
+        try {
+            mGLHandlerThread.join();
+        } catch (InterruptedException e) {
+            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+                    mGLHandlerThread.getName(), mGLHandlerThread.getId()));
+        }
     }
 
     /**
diff --git a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
index f9cf905..b6264dc 100644
--- a/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
+++ b/core/java/android/hardware/camera2/legacy/LegacyCameraDevice.java
@@ -23,18 +23,20 @@
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.utils.LongParcelable;
 import android.hardware.camera2.impl.CameraMetadataNative;
-import android.hardware.camera2.utils.CameraBinderDecorator;
 import android.hardware.camera2.utils.CameraRuntimeException;
 import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.RemoteException;
 import android.util.Log;
+import android.util.Size;
 import android.view.Surface;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.atomic.AtomicInteger;
+
+import static android.hardware.camera2.utils.CameraBinderDecorator.*;
+import static com.android.internal.util.Preconditions.*;
 
 /**
  * This class emulates the functionality of a Camera2 device using a the old Camera class.
@@ -48,18 +50,20 @@
  */
 public class LegacyCameraDevice implements AutoCloseable {
     public static final String DEBUG_PROP = "HAL1ShimLogging";
-
     private final String TAG;
 
+    private static final boolean DEBUG = false;
     private final int mCameraId;
     private final ICameraDeviceCallbacks mDeviceCallbacks;
     private final CameraDeviceState mDeviceState = new CameraDeviceState();
+    private List<Surface> mConfiguredSurfaces;
 
     private final ConditionVariable mIdle = new ConditionVariable(/*open*/true);
-    private final AtomicInteger mRequestIdCounter = new AtomicInteger(0);
 
-    private final HandlerThread mCallbackHandlerThread = new HandlerThread("ResultThread");
+    private final HandlerThread mResultThread = new HandlerThread("ResultThread");
+    private final HandlerThread mCallbackHandlerThread = new HandlerThread("CallbackThread");
     private final Handler mCallbackHandler;
+    private final Handler mResultHandler;
     private static final int ILLEGAL_VALUE = -1;
 
     private CaptureResultExtras getExtrasFromRequest(RequestHolder holder) {
@@ -81,66 +85,108 @@
         public void onError(final int errorCode, RequestHolder holder) {
             mIdle.open();
             final CaptureResultExtras extras = getExtrasFromRequest(holder);
-            try {
-                mDeviceCallbacks.onCameraError(errorCode, extras);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Received remote exception during onCameraError callback: ", e);
-            }
-
+            mResultHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (DEBUG) {
+                        Log.d(TAG, "doing onError callback.");
+                    }
+                    try {
+                        mDeviceCallbacks.onCameraError(errorCode, extras);
+                    } catch (RemoteException e) {
+                        throw new IllegalStateException(
+                                "Received remote exception during onCameraError callback: ", e);
+                    }
+                }
+            });
         }
 
         @Override
         public void onConfiguring() {
             // Do nothing
+            if (DEBUG) {
+                Log.d(TAG, "doing onConfiguring callback.");
+            }
         }
 
         @Override
         public void onIdle() {
             mIdle.open();
 
-            try {
-                mDeviceCallbacks.onCameraIdle();
-            } catch (RemoteException e) {
-                Log.e(TAG, "Received remote exception during onCameraIdle callback: ", e);
-            }
+            mResultHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (DEBUG) {
+                        Log.d(TAG, "doing onIdle callback.");
+                    }
+                    try {
+                        mDeviceCallbacks.onCameraIdle();
+                    } catch (RemoteException e) {
+                        throw new IllegalStateException(
+                                "Received remote exception during onCameraIdle callback: ", e);
+                    }
+                }
+            });
         }
 
         @Override
         public void onCaptureStarted(RequestHolder holder) {
             final CaptureResultExtras extras = getExtrasFromRequest(holder);
 
-            try {
-                // TODO: Don't fake timestamp
-                mDeviceCallbacks.onCaptureStarted(extras, System.nanoTime());
-            } catch (RemoteException e) {
-                Log.e(TAG, "Received remote exception during onCameraError callback: ", e);
-            }
-
+            final long timestamp = System.nanoTime();
+            mResultHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (DEBUG) {
+                        Log.d(TAG, "doing onCaptureStarted callback.");
+                    }
+                    try {
+                        // TODO: Don't fake timestamp
+                        mDeviceCallbacks.onCaptureStarted(extras, timestamp);
+                    } catch (RemoteException e) {
+                        throw new IllegalStateException(
+                                "Received remote exception during onCameraError callback: ", e);
+                    }
+                }
+            });
         }
 
         @Override
-        public void onCaptureResult(CameraMetadataNative result, RequestHolder holder) {
+        public void onCaptureResult(final CameraMetadataNative result, RequestHolder holder) {
             final CaptureResultExtras extras = getExtrasFromRequest(holder);
 
-            try {
-                // TODO: Don't fake metadata
-                mDeviceCallbacks.onResultReceived(result, extras);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Received remote exception during onCameraError callback: ", e);
-            }
+            mResultHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    if (DEBUG) {
+                        Log.d(TAG, "doing onCaptureResult callback.");
+                    }
+                    try {
+                        // TODO: Don't fake metadata
+                        mDeviceCallbacks.onResultReceived(result, extras);
+                    } catch (RemoteException e) {
+                        throw new IllegalStateException(
+                                "Received remote exception during onCameraError callback: ", e);
+                    }
+                }
+            });
         }
     };
 
     private final RequestThreadManager mRequestThreadManager;
 
     /**
-     * Check if a given surface uses {@link ImageFormat#YUV_420_888} format.
+     * Check if a given surface uses {@link ImageFormat#YUV_420_888} or format that can be readily
+     * converted to this; YV12 and NV21 are the two currently supported formats.
      *
      * @param s the surface to check.
-     * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888}.
+     * @return {@code true} if the surfaces uses {@link ImageFormat#YUV_420_888} or a compatible
+     *          format.
      */
     static boolean needsConversion(Surface s) {
-        return LegacyCameraDevice.nativeDetectSurfaceType(s) == ImageFormat.YUV_420_888;
+        int nativeType = LegacyCameraDevice.nativeDetectSurfaceType(s);
+        return nativeType == ImageFormat.YUV_420_888 || nativeType == ImageFormat.YV12 ||
+                nativeType == ImageFormat.NV21;
     }
 
     /**
@@ -161,6 +207,8 @@
         mDeviceCallbacks = callbacks;
         TAG = String.format("CameraDevice-%d-LE", mCameraId);
 
+        mResultThread.start();
+        mResultHandler = new Handler(mResultThread.getLooper());
         mCallbackHandlerThread.start();
         mCallbackHandler = new Handler(mCallbackHandlerThread.getLooper());
         mDeviceState.setCameraDeviceCallbacks(mCallbackHandler, mStateListener);
@@ -172,16 +220,35 @@
     /**
      * Configure the device with a set of output surfaces.
      *
+     * <p>Using empty or {@code null} {@code outputs} is the same as unconfiguring.</p>
+     *
+     * <p>Every surface in {@code outputs} must be non-{@code null}.</p>
+     *
      * @param outputs a list of surfaces to set.
-     * @return an error code for this binder operation, or {@link CameraBinderDecorator.NO_ERROR}
+     * @return an error code for this binder operation, or {@link NO_ERROR}
      *          on success.
      */
     public int configureOutputs(List<Surface> outputs) {
+        if (outputs != null) {
+            for (Surface output : outputs) {
+                if (output == null) {
+                    Log.e(TAG, "configureOutputs - null outputs are not allowed");
+                    return BAD_VALUE;
+                }
+            }
+        }
+
         int error = mDeviceState.setConfiguring();
-        if (error == CameraBinderDecorator.NO_ERROR) {
+        if (error == NO_ERROR) {
             mRequestThreadManager.configure(outputs);
             error = mDeviceState.setIdle();
         }
+
+        // TODO: May also want to check the surfaces more deeply (e.g. state, formats, sizes..)
+        if (error == NO_ERROR) {
+            mConfiguredSurfaces = outputs != null ? new ArrayList<>(outputs) : null;
+        }
+
         return error;
     }
 
@@ -198,7 +265,35 @@
      */
     public int submitRequestList(List<CaptureRequest> requestList, boolean repeating,
             /*out*/LongParcelable frameNumber) {
-        // TODO: validate request here
+        if (requestList == null || requestList.isEmpty()) {
+            Log.e(TAG, "submitRequestList - Empty/null requests are not allowed");
+            return BAD_VALUE;
+        }
+
+        // Make sure that there all requests have at least 1 surface; all surfaces are non-null
+        for (CaptureRequest request : requestList) {
+            if (request.getTargets().isEmpty()) {
+                Log.e(TAG, "submitRequestList - "
+                        + "Each request must have at least one Surface target");
+                return BAD_VALUE;
+            }
+
+            for (Surface surface : request.getTargets()) {
+                if (surface == null) {
+                    Log.e(TAG, "submitRequestList - Null Surface targets are not allowed");
+                    return BAD_VALUE;
+                } else if (mConfiguredSurfaces == null) {
+                    Log.e(TAG, "submitRequestList - must configure " +
+                            " device with valid surfaces before submitting requests");
+                    return INVALID_OPERATION;
+                } else if (!mConfiguredSurfaces.contains(surface)) {
+                    Log.e(TAG, "submitRequestList - cannot use a surface that wasn't configured");
+                    return BAD_VALUE;
+                }
+            }
+        }
+
+        // TODO: further validation of request here
         mIdle.close();
         return mRequestThreadManager.submitCaptureRequests(requestList, repeating,
                 frameNumber);
@@ -244,6 +339,22 @@
     public void close() {
         mRequestThreadManager.quit();
         mCallbackHandlerThread.quitSafely();
+        mResultThread.quitSafely();
+
+        try {
+            mCallbackHandlerThread.join();
+        } catch (InterruptedException e) {
+            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+                    mCallbackHandlerThread.getName(), mCallbackHandlerThread.getId()));
+        }
+
+        try {
+            mResultThread.join();
+        } catch (InterruptedException e) {
+            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+                    mResultThread.getName(), mResultThread.getId()));
+        }
+
         // TODO: throw IllegalStateException in every method after close has been called
     }
 
@@ -258,9 +369,27 @@
         }
     }
 
+    /**
+     * Query the surface for its currently configured default buffer size.
+     * @param surface a non-{@code null} {@code Surface}
+     * @return the width and height of the surface
+     *
+     * @throws NullPointerException if the {@code surface} was {@code null}
+     * @throws IllegalStateException if the {@code surface} was invalid
+     */
+    static Size getSurfaceSize(Surface surface) {
+        checkNotNull(surface);
+
+        int[] dimens = new int[2];
+        nativeDetectSurfaceDimens(surface, /*out*/dimens);
+
+        return new Size(dimens[0], dimens[1]);
+    }
+
     protected static native int nativeDetectSurfaceType(Surface surface);
 
-    protected static native void nativeDetectSurfaceDimens(Surface surface, int[] dimens);
+    protected static native void nativeDetectSurfaceDimens(Surface surface,
+            /*out*/int[/*2*/] dimens);
 
     protected static native void nativeConfigureSurface(Surface surface, int width, int height,
                                                         int pixelFormat);
diff --git a/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
new file mode 100644
index 0000000..f702556
--- /dev/null
+++ b/core/java/android/hardware/camera2/legacy/LegacyMetadataMapper.java
@@ -0,0 +1,623 @@
+/*
+ * Copyright (C) 2014 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.camera2.legacy;
+
+import android.graphics.ImageFormat;
+import android.hardware.Camera;
+import android.hardware.Camera.CameraInfo;
+import android.hardware.Camera.Size;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.params.StreamConfiguration;
+import android.hardware.camera2.params.StreamConfigurationDuration;
+import android.util.Log;
+import android.util.Range;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+import static com.android.internal.util.Preconditions.*;
+import static android.hardware.camera2.CameraCharacteristics.*;
+
+/**
+ * Provide legacy-specific implementations of camera2 metadata for legacy devices, such as the
+ * camera characteristics.
+ */
+public class LegacyMetadataMapper {
+    private static final String TAG = "LegacyMetadataMapper";
+    private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+    // from graphics.h
+    private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22;
+    private static final int HAL_PIXEL_FORMAT_BLOB = 0x21;
+
+    // for metadata
+    private static final float LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS = 0.0f;
+
+    private static final long APPROXIMATE_CAPTURE_DELAY_MS = 200; // ms
+    private static final long APPROXIMATE_SENSOR_AREA = (1 << 23); // 8mp
+    private static final long APPROXIMATE_JPEG_ENCODE_TIME = 600; // ms
+    private static final long NS_PER_MS = 1000000;
+
+    /*
+     * Development hijinks: Lie about not supporting certain capabilities
+     *
+     * - Unblock some CTS tests from running whose main intent is not the metadata itself
+     *
+     * TODO: Remove these constants and strip out any code that previously relied on them
+     * being set to true.
+     */
+    private static final boolean LIE_ABOUT_FLASH = true;
+    private static final boolean LIE_ABOUT_AE = true;
+    private static final boolean LIE_ABOUT_AF = true;
+    private static final boolean LIE_ABOUT_AWB = true;
+
+    /**
+     * Create characteristics for a legacy device by mapping the {@code parameters}
+     * and {@code info}
+     *
+     * @param parameters A non-{@code null} parameters set
+     * @param info Camera info with camera facing direction and angle of orientation
+     *
+     * @return static camera characteristics for a camera device
+     *
+     * @throws NullPointerException if any of the args were {@code null}
+     */
+    public static CameraCharacteristics createCharacteristics(Camera.Parameters parameters,
+            CameraInfo info) {
+        checkNotNull(parameters, "parameters must not be null");
+        checkNotNull(info, "info must not be null");
+
+        String paramStr = parameters.flatten();
+        android.hardware.CameraInfo outerInfo = new android.hardware.CameraInfo();
+        outerInfo.info = info;
+
+        return createCharacteristics(paramStr, outerInfo);
+    }
+
+    /**
+     * Create characteristics for a legacy device by mapping the {@code parameters}
+     * and {@code info}
+     *
+     * @param parameters A string parseable by {@link Camera.Parameters#unflatten}
+     * @param info Camera info with camera facing direction and angle of orientation
+     * @return static camera characteristics for a camera device
+     *
+     * @throws NullPointerException if any of the args were {@code null}
+     */
+    public static CameraCharacteristics createCharacteristics(String parameters,
+            android.hardware.CameraInfo info) {
+        checkNotNull(parameters, "parameters must not be null");
+        checkNotNull(info, "info must not be null");
+        checkNotNull(info.info, "info.info must not be null");
+
+        CameraMetadataNative m = new CameraMetadataNative();
+
+        mapCameraInfo(m, info.info);
+
+        Camera.Parameters params = Camera.getEmptyParameters();
+        params.unflatten(parameters);
+        mapCameraParameters(m, params);
+
+        if (VERBOSE) {
+            Log.v(TAG, "createCharacteristics metadata:");
+            Log.v(TAG, "--------------------------------------------------- (start)");
+            m.dumpToLog();
+            Log.v(TAG, "--------------------------------------------------- (end)");
+        }
+
+        return new CameraCharacteristics(m);
+    }
+
+    private static void mapCameraInfo(CameraMetadataNative m, CameraInfo i) {
+        m.set(LENS_FACING, i.facing == CameraInfo.CAMERA_FACING_BACK ?
+                LENS_FACING_BACK : LENS_FACING_FRONT);
+        m.set(SENSOR_ORIENTATION, i.orientation);
+    }
+
+    private static void mapCameraParameters(CameraMetadataNative m, Camera.Parameters p) {
+        m.set(INFO_SUPPORTED_HARDWARE_LEVEL, INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED);
+        mapStreamConfigs(m, p);
+        mapControlAe(m, p);
+        mapControlAwb(m, p);
+        mapCapabilities(m, p);
+        mapLens(m, p);
+        mapFlash(m, p);
+        // TODO: map other fields
+    }
+
+    private static void mapStreamConfigs(CameraMetadataNative m, Camera.Parameters p) {
+
+        ArrayList<StreamConfiguration> availableStreamConfigs = new ArrayList<>();
+        /*
+         * Implementation-defined (preview, recording, etc) -> use camera1 preview sizes
+         * YUV_420_888 cpu callbacks -> use camera1 preview sizes
+         * Other preview callbacks (CPU) -> use camera1 preview sizes
+         * JPEG still capture -> use camera1 still capture sizes
+         *
+         * Use platform-internal format constants here, since StreamConfigurationMap does the
+         * remapping to public format constants.
+         */
+        List<Size> previewSizes = p.getSupportedPreviewSizes();
+        appendStreamConfig(availableStreamConfigs,
+                HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED, previewSizes);
+        appendStreamConfig(availableStreamConfigs,
+                ImageFormat.YUV_420_888, previewSizes);
+        for (int format : p.getSupportedPreviewFormats()) {
+            if (ImageFormat.isPublicFormat(format)) {
+                appendStreamConfig(availableStreamConfigs, format, previewSizes);
+            } else {
+                /*
+                 *  Do not add any formats unknown to us
+                 * (since it would fail runtime checks in StreamConfigurationMap)
+                 */
+                Log.w(TAG,
+                        String.format("mapStreamConfigs - Skipping non-public format %x", format));
+            }
+        }
+
+        List<Camera.Size> jpegSizes = p.getSupportedPictureSizes();
+        appendStreamConfig(availableStreamConfigs,
+                HAL_PIXEL_FORMAT_BLOB, p.getSupportedPictureSizes());
+        m.set(SCALER_AVAILABLE_STREAM_CONFIGURATIONS,
+                availableStreamConfigs.toArray(new StreamConfiguration[0]));
+
+        // No frame durations available
+        m.set(SCALER_AVAILABLE_MIN_FRAME_DURATIONS, new StreamConfigurationDuration[0]);
+
+        StreamConfigurationDuration[] jpegStalls =
+                new StreamConfigurationDuration[jpegSizes.size()];
+        int i = 0;
+        long longestStallDuration = -1;
+        for (Camera.Size s : jpegSizes) {
+            long stallDuration =  calculateJpegStallDuration(s);
+            jpegStalls[i++] = new StreamConfigurationDuration(HAL_PIXEL_FORMAT_BLOB, s.width,
+                    s.height, stallDuration);
+            if (longestStallDuration < stallDuration) {
+                longestStallDuration = stallDuration;
+            }
+        }
+        // Set stall durations for jpeg, other formats use default stall duration
+        m.set(SCALER_AVAILABLE_STALL_DURATIONS, jpegStalls);
+
+        m.set(SENSOR_INFO_MAX_FRAME_DURATION, longestStallDuration);
+    }
+
+    @SuppressWarnings({"unchecked"})
+    private static void mapControlAe(CameraMetadataNative m, Camera.Parameters p) {
+        /*
+         * control.aeAvailableTargetFpsRanges
+         */
+        List<int[]> fpsRanges = p.getSupportedPreviewFpsRange();
+        if (fpsRanges == null) {
+            throw new AssertionError("Supported FPS ranges cannot be null.");
+        }
+        int rangesSize = fpsRanges.size();
+        if (rangesSize <= 0) {
+            throw new AssertionError("At least one FPS range must be supported.");
+        }
+        Range<Integer>[] ranges = new Range[rangesSize];
+        int i = 0;
+        for (int[] r : fpsRanges) {
+            ranges[i++] = Range.create(r[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+                    r[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+        }
+        m.set(CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES, ranges);
+
+        /*
+         * control.aeAvailableAntiBandingModes
+         */
+
+        List<String> antiBandingModes = p.getSupportedAntibanding();
+        int antiBandingModesSize = antiBandingModes.size();
+        if (antiBandingModesSize > 0) {
+            int[] modes = new int[antiBandingModesSize];
+            int j = 0;
+            for (String mode : antiBandingModes) {
+                int convertedMode = convertAntiBandingMode(mode);
+                if (convertedMode == -1) {
+                    Log.w(TAG, "Antibanding mode " + ((mode == null) ? "NULL" : mode) +
+                            " not supported, skipping...");
+                } else {
+                    modes[j++] = convertedMode;
+                }
+            }
+            m.set(CONTROL_AE_AVAILABLE_ANTIBANDING_MODES, Arrays.copyOf(modes, j));
+        }
+
+        /*
+         * control.aeAvailableModes
+         */
+        List<String> flashModes = p.getSupportedFlashModes();
+
+        String[] flashModeStrings = new String[] {
+                Camera.Parameters.FLASH_MODE_AUTO,
+                Camera.Parameters.FLASH_MODE_ON,
+                Camera.Parameters.FLASH_MODE_RED_EYE,
+                // Map these manually
+                Camera.Parameters.FLASH_MODE_TORCH,
+                Camera.Parameters.FLASH_MODE_OFF,
+        };
+        int[] flashModeInts = new int[] {
+                CONTROL_AE_MODE_ON,
+                CONTROL_AE_MODE_ON_AUTO_FLASH,
+                CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE
+        };
+        int[] aeAvail = convertStringListToIntArray(flashModes, flashModeStrings, flashModeInts);
+
+        // No flash control -> AE is always on
+        if (aeAvail == null || aeAvail.length == 0) {
+            aeAvail = new int[] {
+                    CONTROL_AE_MODE_ON
+            };
+        }
+
+        if (LIE_ABOUT_FLASH) {
+            // TODO: Remove this branch
+            Log.w(TAG, "mapControlAe - lying; saying we only support CONTROL_AE_MODE_ON");
+            aeAvail = new int[] {
+                    CONTROL_AE_MODE_ON
+            };
+        }
+
+        m.set(CONTROL_AE_AVAILABLE_MODES, aeAvail);
+    }
+
+    private static void mapControlAwb(CameraMetadataNative m, Camera.Parameters p) {
+        if (!LIE_ABOUT_AWB) {
+            throw new AssertionError("Not implemented yet");
+        }
+    }
+
+    private static void mapCapabilities(CameraMetadataNative m, Camera.Parameters p) {
+        int[] capabilities = { REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE };
+        m.set(REQUEST_AVAILABLE_CAPABILITIES, capabilities);
+    }
+
+    private static void mapLens(CameraMetadataNative m, Camera.Parameters p) {
+        /*
+         *  We can tell if the lens is fixed focus;
+         *  but if it's not, we can't tell the minimum focus distance, so leave it null then.
+         */
+        if (p.getFocusMode() == Camera.Parameters.FOCUS_MODE_FIXED) {
+            m.set(LENS_INFO_MINIMUM_FOCUS_DISTANCE, LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS);
+        }
+    }
+
+    private static void mapFlash(CameraMetadataNative m, Camera.Parameters p) {
+        boolean flashAvailable = false;
+        List<String> supportedFlashModes = p.getSupportedFlashModes();
+        if (supportedFlashModes != null) {
+            // If only 'OFF' is available, we don't really have flash support
+            if (!(supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_OFF) &&
+                    supportedFlashModes.size() == 1)) {
+                flashAvailable = true;
+            }
+        }
+
+        if (LIE_ABOUT_FLASH && flashAvailable) {
+            // TODO: remove this branch
+            Log.w(TAG, "mapFlash - lying; saying we never support flash");
+            flashAvailable = false;
+        }
+
+        m.set(FLASH_INFO_AVAILABLE, flashAvailable);
+    }
+
+    private static void appendStreamConfig(
+            ArrayList<StreamConfiguration> configs, int format, List<Camera.Size> sizes) {
+        for (Camera.Size size : sizes) {
+            StreamConfiguration config =
+                    new StreamConfiguration(format, size.width, size.height, /*input*/false);
+            configs.add(config);
+        }
+    }
+
+    /**
+     * Returns -1 if the anti-banding mode string is null, or not supported.
+     */
+    private static int convertAntiBandingMode(final String mode) {
+        if (mode == null) {
+            return -1;
+        }
+        switch(mode) {
+            case Camera.Parameters.ANTIBANDING_OFF: {
+                return CONTROL_AE_ANTIBANDING_MODE_OFF;
+            }
+            case Camera.Parameters.ANTIBANDING_50HZ: {
+                return CONTROL_AE_ANTIBANDING_MODE_50HZ;
+            }
+            case Camera.Parameters.ANTIBANDING_60HZ: {
+                return CONTROL_AE_ANTIBANDING_MODE_60HZ;
+            }
+            case Camera.Parameters.ANTIBANDING_AUTO: {
+                return CONTROL_AE_ANTIBANDING_MODE_AUTO;
+            }
+            default: {
+                return -1;
+            }
+        }
+    }
+
+    /**
+     * Returns null if the anti-banding mode enum is not supported.
+     */
+    private static String convertAntiBandingModeToLegacy(int mode) {
+        switch(mode) {
+            case CONTROL_AE_ANTIBANDING_MODE_OFF: {
+                return Camera.Parameters.ANTIBANDING_OFF;
+            }
+            case CONTROL_AE_ANTIBANDING_MODE_50HZ: {
+                return Camera.Parameters.ANTIBANDING_50HZ;
+            }
+            case CONTROL_AE_ANTIBANDING_MODE_60HZ: {
+                return Camera.Parameters.ANTIBANDING_60HZ;
+            }
+            case CONTROL_AE_ANTIBANDING_MODE_AUTO: {
+                return Camera.Parameters.ANTIBANDING_AUTO;
+            }
+            default: {
+                return null;
+            }
+        }
+    }
+
+
+    private static int[] convertAeFpsRangeToLegacy(Range<Integer> fpsRange) {
+        int[] legacyFps = new int[2];
+        legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX] = fpsRange.getLower();
+        legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX] = fpsRange.getUpper();
+        return legacyFps;
+    }
+
+    /**
+     * Return the stall duration for a given output jpeg size in nanoseconds.
+     *
+     * <p>An 8mp image is chosen to have a stall duration of 0.8 seconds.</p>
+     */
+    private static long calculateJpegStallDuration(Camera.Size size) {
+        long baseDuration = APPROXIMATE_CAPTURE_DELAY_MS * NS_PER_MS; // 200ms for capture
+        long area = size.width * (long) size.height;
+        long stallPerArea = APPROXIMATE_JPEG_ENCODE_TIME * NS_PER_MS /
+                APPROXIMATE_SENSOR_AREA; // 600ms stall for 8mp
+        return baseDuration + area * stallPerArea;
+    }
+
+    /**
+     * Generate capture result metadata from legacy camera parameters.
+     *
+     * @param params a {@link Camera.Parameters} object to generate metadata from.
+     * @param request the {@link CaptureRequest} used for this result.
+     * @param timestamp the timestamp to use for this result in nanoseconds.
+     * @return a {@link CameraMetadataNative} object containing result metadata.
+     */
+    public static CameraMetadataNative convertResultMetadata(Camera.Parameters params,
+                                                      CaptureRequest request,
+                                                      long timestamp) {
+        CameraMetadataNative result = new CameraMetadataNative();
+
+        /*
+         * control
+         */
+        // control.afState
+        if (LIE_ABOUT_AF) {
+            // TODO: Implement autofocus state machine
+            result.set(CaptureResult.CONTROL_AF_MODE, request.get(CaptureRequest.CONTROL_AF_MODE));
+        }
+
+        // control.aeState
+        if (LIE_ABOUT_AE) {
+            // Lie to pass CTS temporarily.
+            // TODO: Implement precapture trigger, after which we can report CONVERGED ourselves
+            result.set(CaptureResult.CONTROL_AE_STATE,
+                    CONTROL_AE_STATE_CONVERGED);
+
+            result.set(CaptureResult.CONTROL_AE_MODE,
+                    request.get(CaptureRequest.CONTROL_AE_MODE));
+        }
+
+        // control.awbLock
+        result.set(CaptureResult.CONTROL_AWB_LOCK, params.getAutoWhiteBalanceLock());
+
+        // control.awbState
+        if (LIE_ABOUT_AWB) {
+            // Lie to pass CTS temporarily.
+            // TODO: CTS needs to be updated not to query this value
+            // for LIMITED devices unless its guaranteed to be available.
+            result.set(CaptureResult.CONTROL_AWB_STATE,
+                    CameraMetadata.CONTROL_AWB_STATE_CONVERGED);
+            // TODO: Read the awb mode from parameters instead
+            result.set(CaptureResult.CONTROL_AWB_MODE,
+                    request.get(CaptureRequest.CONTROL_AWB_MODE));
+        }
+
+        /*
+         * lens
+         */
+        // lens.focalLength
+        result.set(CaptureResult.LENS_FOCAL_LENGTH, params.getFocalLength());
+
+        /*
+         * sensor
+         */
+        // sensor.timestamp
+        result.set(CaptureResult.SENSOR_TIMESTAMP, timestamp);
+
+        // TODO: Remaining result metadata tags conversions.
+        return result;
+    }
+
+    /**
+     * Set the legacy parameters using the request metadata.
+     *
+     * @param request a {@link CaptureRequest} object to generate parameters from.
+     * @param params the a {@link Camera.Parameters} to set parameters in.
+     */
+    public static void convertRequestMetadata(CaptureRequest request,
+            /*out*/Camera.Parameters params) {
+
+        /*
+         * control.ae*
+         */
+        // control.aeAntibandingMode
+        Integer antiBandingMode = request.get(CaptureRequest.CONTROL_AE_ANTIBANDING_MODE);
+        if (antiBandingMode != null) {
+            String legacyMode = convertAntiBandingModeToLegacy(antiBandingMode);
+            if (legacyMode != null) params.setAntibanding(legacyMode);
+        }
+
+        // control.aeTargetFpsRange
+        Range<Integer> aeFpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+        if (aeFpsRange != null) {
+            int[] legacyFps = convertAeFpsRangeToLegacy(aeFpsRange);
+            params.setPreviewFpsRange(legacyFps[Camera.Parameters.PREVIEW_FPS_MIN_INDEX],
+                    legacyFps[Camera.Parameters.PREVIEW_FPS_MAX_INDEX]);
+        }
+
+        /*
+         * control
+         */
+        // control.awbLock
+        Boolean awbLock = request.get(CaptureRequest.CONTROL_AWB_LOCK);
+        params.setAutoWhiteBalanceLock(awbLock == null ? false : awbLock);
+    }
+
+    /**
+     * Create an int[] from the List<> by using {@code convertFrom} and {@code convertTo}
+     * as a one-to-one map (via the index).
+     *
+     * <p>Strings not appearing in {@code convertFrom} are ignored (with a warning);
+     * strings appearing in {@code convertFrom} but not {@code convertTo} are silently
+     * dropped.</p>
+     *
+     * @param list Source list of strings
+     * @param convertFrom Conversion list of strings
+     * @param convertTo Conversion list of ints
+     * @return An array of ints where the values correspond to the ones in {@code convertTo}
+     *         or {@code null} if {@code list} was {@code null}
+     */
+    private static int[] convertStringListToIntArray(
+            List<String> list, String[] convertFrom, int[] convertTo) {
+        if (list == null) {
+            return null;
+        }
+
+        List<Integer> convertedList = new ArrayList<>(list.size());
+
+        for (String str : list) {
+            int strIndex = getArrayIndex(convertFrom, str);
+
+            // Guard against bad API1 values
+            if (strIndex < 0) {
+                Log.w(TAG, "Ignoring invalid parameter " + str);
+                continue;
+            }
+
+            // Ignore values we can't map into (intentional)
+            if (strIndex < convertTo.length) {
+                convertedList.add(convertTo[strIndex]);
+            }
+        }
+
+        int[] returnArray = new int[convertedList.size()];
+        for (int i = 0; i < returnArray.length; ++i) {
+            returnArray[i] = convertedList.get(i);
+        }
+
+        return returnArray;
+    }
+
+    /** Return the index of {@code needle} in the {@code array}, or else {@code -1} */
+    private static <T> int getArrayIndex(T[] array, T needle) {
+        if (needle == null) {
+            return -1;
+        }
+
+        int index = 0;
+        for (T elem : array) {
+            if (Objects.equals(elem, needle)) {
+                return index;
+            }
+            index++;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Create a request template
+     *
+     * @param c a non-{@code null} camera characteristics for this camera
+     * @param templateId a non-negative template ID
+     *
+     * @return a non-{@code null} request template
+     *
+     * @throws IllegalArgumentException if {@code templateId} was invalid
+     *
+     * @see android.hardware.camera2.CameraDevice#TEMPLATE_MANUAL
+     */
+    public static CameraMetadataNative createRequestTemplate(
+            CameraCharacteristics c, int templateId) {
+        if (templateId < 0 || templateId > CameraDevice.TEMPLATE_MANUAL) {
+            throw new IllegalArgumentException("templateId out of range");
+        }
+
+        CameraMetadataNative m = new CameraMetadataNative();
+
+        /*
+         * NOTE: If adding new code here and it needs to query the static info,
+         * query the camera characteristics, so we can reuse this for api2 code later
+         * to create our own templates in the framework
+         */
+
+        if (LIE_ABOUT_AWB) {
+            m.set(CaptureRequest.CONTROL_AWB_MODE, CameraMetadata.CONTROL_AWB_MODE_AUTO);
+        } else {
+            throw new AssertionError("Valid control.awbMode not implemented yet");
+        }
+
+        // control.aeMode
+        m.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON);
+        // AE is always unconditionally available in API1 devices
+
+        // control.afMode
+        {
+            Float minimumFocusDistance = c.get(LENS_INFO_MINIMUM_FOCUS_DISTANCE);
+
+            int afMode;
+            if (minimumFocusDistance != null &&
+                    minimumFocusDistance == LENS_INFO_MINIMUM_FOCUS_DISTANCE_FIXED_FOCUS) {
+                // Cannot control auto-focus with fixed-focus cameras
+                afMode = CameraMetadata.CONTROL_AF_MODE_OFF;
+            } else {
+                // If a minimum focus distance is reported; the camera must have AF
+                afMode = CameraMetadata.CONTROL_AF_MODE_AUTO;
+            }
+
+            m.set(CaptureRequest.CONTROL_AF_MODE, afMode);
+        }
+
+        // TODO: map other request template values
+        return m;
+    }
+}
diff --git a/core/java/android/hardware/camera2/legacy/RequestQueue.java b/core/java/android/hardware/camera2/legacy/RequestQueue.java
index 5c68303..7820648 100644
--- a/core/java/android/hardware/camera2/legacy/RequestQueue.java
+++ b/core/java/android/hardware/camera2/legacy/RequestQueue.java
@@ -77,7 +77,8 @@
         long ret = INVALID_FRAME;
         if (mRepeatingRequest != null && mRepeatingRequest.getRequestId() == requestId) {
             mRepeatingRequest = null;
-            ret = mCurrentRepeatingFrameNumber;
+            ret = (mCurrentRepeatingFrameNumber == INVALID_FRAME) ? INVALID_FRAME :
+                    mCurrentRepeatingFrameNumber - 1;
             mCurrentRepeatingFrameNumber = INVALID_FRAME;
         } else {
             Log.e(TAG, "cancel failed: no repeating request exists for request id: " + requestId);
@@ -105,7 +106,8 @@
         long ret = INVALID_FRAME;
         if (burst.isRepeating()) {
             if (mRepeatingRequest != null) {
-                ret = mCurrentRepeatingFrameNumber;
+                ret = (mCurrentRepeatingFrameNumber == INVALID_FRAME) ? INVALID_FRAME :
+                        mCurrentRepeatingFrameNumber - 1;
             }
             mCurrentRepeatingFrameNumber = INVALID_FRAME;
             mRepeatingRequest = burst;
@@ -122,7 +124,7 @@
         for (BurstHolder b : mRequestQueue) {
             total += b.getNumberOfRequests();
             if (b.getRequestId() == requestId) {
-                return total;
+                return total - 1;
             }
         }
         throw new IllegalStateException(
diff --git a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
index c4669f5..efc2b0e 100644
--- a/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
+++ b/core/java/android/hardware/camera2/legacy/RequestThreadManager.java
@@ -16,7 +16,6 @@
 
 package android.hardware.camera2.legacy;
 
-import android.graphics.ImageFormat;
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
 import android.hardware.camera2.CaptureRequest;
@@ -28,12 +27,15 @@
 import android.os.SystemClock;
 import android.util.Log;
 import android.util.Pair;
+import android.util.Size;
 import android.view.Surface;
 
 import java.io.IOError;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.List;
 
 /**
@@ -62,23 +64,31 @@
     private static final int MSG_CLEANUP = 3;
 
     private static final int PREVIEW_FRAME_TIMEOUT = 300; // ms
-    private static final int JPEG_FRAME_TIMEOUT = 1000; // ms
+    private static final int JPEG_FRAME_TIMEOUT = 3000; // ms (same as CTS for API2)
 
+    private static final float ASPECT_RATIO_TOLERANCE = 0.01f;
     private boolean mPreviewRunning = false;
 
+    private volatile long mLastJpegTimestamp;
+    private volatile long mLastPreviewTimestamp;
     private volatile RequestHolder mInFlightPreview;
     private volatile RequestHolder mInFlightJpeg;
 
-    private List<Surface> mPreviewOutputs = new ArrayList<Surface>();
-    private List<Surface> mCallbackOutputs = new ArrayList<Surface>();
+    private final List<Surface> mPreviewOutputs = new ArrayList<Surface>();
+    private final List<Surface> mCallbackOutputs = new ArrayList<Surface>();
     private GLThreadManager mGLThreadManager;
     private SurfaceTexture mPreviewTexture;
+    private Camera.Parameters mParams;
+
+    private Size mIntermediateBufferSize;
 
     private final RequestQueue mRequestQueue = new RequestQueue();
+    private CaptureRequest mLastRequest = null;
     private SurfaceTexture mDummyTexture;
     private Surface mDummySurface;
 
     private final FpsCounter mPrevCounter = new FpsCounter("Incoming Preview");
+    private final FpsCounter mRequestCounter = new FpsCounter("Incoming Requests");
 
     /**
      * Container object for Configure messages.
@@ -93,6 +103,31 @@
         }
     }
 
+
+    /**
+     * Comparator for {@link Size} objects by the area.
+     *
+     * <p>This comparator totally orders by rectangle area. Tiebreaks on width.</p>
+     */
+    private static class SizeAreaComparator implements Comparator<Size> {
+        @Override
+        public int compare(Size size, Size size2) {
+            if (size == null || size2 == null) {
+                throw new NullPointerException("Null argument passed to compare");
+            }
+            if (size.equals(size2)) return 0;
+            long width = size.getWidth();
+            long width2 = size2.getWidth();
+            long area = width * size.getHeight();
+            long area2 = width2 * size2.getHeight();
+            if (area == area2) {
+                return (width > width2) ? 1 : -1;
+            }
+            return (area > area2) ? 1 : -1;
+
+        }
+    }
+
     /**
      * Counter class used to calculate and log the current FPS of frame production.
      */
@@ -177,23 +212,37 @@
         }
     };
 
+    private final Camera.ShutterCallback mJpegShutterCallback = new Camera.ShutterCallback() {
+        @Override
+        public void onShutter() {
+            mLastJpegTimestamp = SystemClock.elapsedRealtimeNanos();
+        }
+    };
+
     private final SurfaceTexture.OnFrameAvailableListener mPreviewCallback =
             new SurfaceTexture.OnFrameAvailableListener() {
                 @Override
                 public void onFrameAvailable(SurfaceTexture surfaceTexture) {
+                    RequestHolder holder = mInFlightPreview;
+                    if (holder == null) {
+                        mGLThreadManager.queueNewFrame(null);
+                        Log.w(TAG, "Dropping preview frame.");
+                        return;
+                    }
+
                     if (DEBUG) {
                         mPrevCounter.countAndLog();
                     }
-                    RequestHolder holder = mInFlightPreview;
-                    if (holder == null) {
-                        Log.w(TAG, "Dropping preview frame.");
-                        mInFlightPreview = null;
-                        return;
-                    }
+                    mInFlightPreview = null;
+
                     if (holder.hasPreviewTargets()) {
                         mGLThreadManager.queueNewFrame(holder.getHolderTargets());
                     }
 
+                    /**
+                     * TODO: Get timestamp from GL thread after buffer update.
+                     */
+                    mLastPreviewTimestamp = surfaceTexture.getTimestamp();
                     mReceivedPreview.open();
                 }
             };
@@ -213,14 +262,18 @@
     }
 
     private void doJpegCapture(RequestHolder request) throws IOException {
+        if (DEBUG) Log.d(TAG, "doJpegCapture");
+
         if (!mPreviewRunning) {
+            if (DEBUG) Log.d(TAG, "doJpegCapture - create fake surface");
+
             createDummySurface();
             mCamera.setPreviewTexture(mDummyTexture);
             startPreview();
         }
         mInFlightJpeg = request;
         // TODO: Hook up shutter callback to CameraDeviceStateListener#onCaptureStarted
-        mCamera.takePicture(/*shutter*/null, /*raw*/null, mJpegCallback);
+        mCamera.takePicture(mJpegShutterCallback, /*raw*/null, mJpegCallback);
         mPreviewRunning = false;
     }
 
@@ -230,7 +283,13 @@
             return; // Already running
         }
 
-        mPreviewTexture.setDefaultBufferSize(640, 480); // TODO: size selection based on request
+        if (mPreviewTexture == null) {
+            throw new IllegalStateException(
+                    "Preview capture called with no preview surfaces configured.");
+        }
+
+        mPreviewTexture.setDefaultBufferSize(mIntermediateBufferSize.getWidth(),
+                mIntermediateBufferSize.getHeight());
         mCamera.setPreviewTexture(mPreviewTexture);
         Camera.Parameters params = mCamera.getParameters();
         List<int[]> supportedFpsRanges = params.getSupportedPreviewFpsRange();
@@ -248,6 +307,7 @@
         startPreview();
     }
 
+
     private void configureOutputs(Collection<Surface> outputs) throws IOException {
         stopPreview();
         if (mGLThreadManager != null) {
@@ -261,17 +321,73 @@
         mInFlightPreview = null;
         mInFlightJpeg = null;
 
-        for (Surface s : outputs) {
-            int format = LegacyCameraDevice.nativeDetectSurfaceType(s);
-            switch (format) {
-                case CameraMetadataNative.NATIVE_JPEG_FORMAT:
-                    mCallbackOutputs.add(s);
-                    break;
-                default:
-                    mPreviewOutputs.add(s);
-                    break;
+        if (outputs != null) {
+            for (Surface s : outputs) {
+                int format = LegacyCameraDevice.nativeDetectSurfaceType(s);
+                switch (format) {
+                    case CameraMetadataNative.NATIVE_JPEG_FORMAT:
+                        mCallbackOutputs.add(s);
+                        break;
+                    default:
+                        mPreviewOutputs.add(s);
+                        break;
+                }
             }
         }
+        mParams = mCamera.getParameters();
+        if (mPreviewOutputs.size() > 0) {
+            List<Size> outputSizes = new ArrayList<>(outputs.size());
+            for (Surface s : mPreviewOutputs) {
+                int[] dimens = {0, 0};
+                LegacyCameraDevice.nativeDetectSurfaceDimens(s, dimens);
+                outputSizes.add(new Size(dimens[0], dimens[1]));
+            }
+
+            Size largestOutput = findLargestByArea(outputSizes);
+
+            // Find largest jpeg dimension - assume to have the same aspect ratio as sensor.
+            List<Size> supportedJpegSizes = convertSizeList(mParams.getSupportedPictureSizes());
+            Size largestJpegDimen = findLargestByArea(supportedJpegSizes);
+
+            List<Size> supportedPreviewSizes = convertSizeList(mParams.getSupportedPreviewSizes());
+
+            // Use smallest preview dimension with same aspect ratio as sensor that is >= than all
+            // of the configured output dimensions.  If none exists, fall back to using the largest
+            // supported preview size.
+            long largestOutputArea = largestOutput.getHeight() * (long) largestOutput.getWidth();
+            Size bestPreviewDimen = findLargestByArea(supportedPreviewSizes);
+            for (Size s : supportedPreviewSizes) {
+                long currArea = s.getWidth() * s.getHeight();
+                long bestArea = bestPreviewDimen.getWidth() * bestPreviewDimen.getHeight();
+                if (checkAspectRatiosMatch(largestJpegDimen, s) && (currArea < bestArea &&
+                        currArea >= largestOutputArea)) {
+                    bestPreviewDimen = s;
+                }
+            }
+
+            mIntermediateBufferSize = bestPreviewDimen;
+            if (DEBUG) {
+                Log.d(TAG, "Intermediate buffer selected with dimens: " +
+                        bestPreviewDimen.toString());
+            }
+        } else {
+            mIntermediateBufferSize = null;
+            if (DEBUG) {
+                Log.d(TAG, "No Intermediate buffer selected, no preview outputs were configured");
+            }
+        }
+
+        Size smallestSupportedJpegSize = calculatePictureSize(mCallbackOutputs, mParams);
+        if (smallestSupportedJpegSize != null) {
+            /*
+             * Set takePicture size to the smallest supported JPEG size large enough
+             * to scale/crop out of for the bounding rectangle of the configured JPEG sizes.
+             */
+
+            Log.i(TAG, "configureOutputs - set take picture size to " + smallestSupportedJpegSize);
+            mParams.setPictureSize(
+                    smallestSupportedJpegSize.getWidth(), smallestSupportedJpegSize.getHeight());
+        }
 
         // TODO: Detect and optimize single-output paths here to skip stream teeing.
         if (mGLThreadManager == null) {
@@ -282,7 +398,123 @@
         mGLThreadManager.setConfigurationAndWait(mPreviewOutputs);
         mGLThreadManager.allowNewFrames();
         mPreviewTexture = mGLThreadManager.getCurrentSurfaceTexture();
-        mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
+        if (mPreviewTexture != null) {
+            mPreviewTexture.setOnFrameAvailableListener(mPreviewCallback);
+        }
+
+        // TODO: configure the JPEG surface with some arbitrary size
+        // using LegacyCameraDevice.nativeConfigureSurface
+    }
+
+    /**
+     * Find a JPEG size (that is supported by the legacy camera device) which is equal to or larger
+     * than all of the configured {@code JPEG} outputs (by both width and height).
+     *
+     * <p>If multiple supported JPEG sizes are larger, select the smallest of them which
+     * still satisfies the above constraint.</p>
+     *
+     * <p>As a result, the returned size is guaranteed to be usable without needing
+     * to upscale any of the outputs. If only one {@code JPEG} surface is used,
+     * then no scaling/cropping is necessary between the taken picture and
+     * the {@code JPEG} output surface.</p>
+     *
+     * @param callbackOutputs a non-{@code null} list of {@code Surface}s with any image formats
+     * @param params api1 parameters (used for reading only)
+     *
+     * @return a size large enough to fit all of the configured {@code JPEG} outputs, or
+     *          {@code null} if the {@code callbackOutputs} did not have any {@code JPEG}
+     *          surfaces.
+     */
+    private Size calculatePictureSize(
+            Collection<Surface> callbackOutputs, Camera.Parameters params) {
+        /*
+         * Find the largest JPEG size (if any), from the configured outputs:
+         * - the api1 picture size should be set to the smallest legal size that's at least as large
+         *   as the largest configured JPEG size
+         */
+        List<Size> configuredJpegSizes = new ArrayList<Size>();
+        for (Surface callbackSurface : callbackOutputs) {
+            int format = LegacyCameraDevice.nativeDetectSurfaceType(callbackSurface);
+
+            if (format != CameraMetadataNative.NATIVE_JPEG_FORMAT) {
+                continue; // Ignore non-JPEG callback formats
+            }
+
+            Size jpegSize = LegacyCameraDevice.getSurfaceSize(callbackSurface);
+            configuredJpegSizes.add(jpegSize);
+        }
+        if (!configuredJpegSizes.isEmpty()) {
+            /*
+             * Find the largest configured JPEG width, and height, independently
+             * of the rest.
+             *
+             * The rest of the JPEG streams can be cropped out of this smallest bounding
+             * rectangle.
+             */
+            int maxConfiguredJpegWidth = -1;
+            int maxConfiguredJpegHeight = -1;
+            for (Size jpegSize : configuredJpegSizes) {
+                maxConfiguredJpegWidth = jpegSize.getWidth() > maxConfiguredJpegWidth ?
+                        jpegSize.getWidth() : maxConfiguredJpegWidth;
+                maxConfiguredJpegHeight = jpegSize.getHeight() > maxConfiguredJpegHeight ?
+                        jpegSize.getHeight() : maxConfiguredJpegHeight;
+            }
+            Size smallestBoundJpegSize = new Size(maxConfiguredJpegWidth, maxConfiguredJpegHeight);
+
+            List<Size> supportedJpegSizes = convertSizeList(params.getSupportedPictureSizes());
+
+            /*
+             * Find the smallest supported JPEG size that can fit the smallest bounding
+             * rectangle for the configured JPEG sizes.
+             */
+            List<Size> candidateSupportedJpegSizes = new ArrayList<>();
+            for (Size supportedJpegSize : supportedJpegSizes) {
+                if (supportedJpegSize.getWidth() >= maxConfiguredJpegWidth &&
+                    supportedJpegSize.getHeight() >= maxConfiguredJpegHeight) {
+                    candidateSupportedJpegSizes.add(supportedJpegSize);
+                }
+            }
+
+            if (candidateSupportedJpegSizes.isEmpty()) {
+                throw new AssertionError(
+                        "Could not find any supported JPEG sizes large enough to fit " +
+                        smallestBoundJpegSize);
+            }
+
+            Size smallestSupportedJpegSize = Collections.min(candidateSupportedJpegSizes,
+                    new SizeAreaComparator());
+
+            if (!smallestSupportedJpegSize.equals(smallestBoundJpegSize)) {
+                Log.w(TAG,
+                        String.format(
+                                "configureOutputs - Will need to crop picture %s into "
+                                + "smallest bound size %s",
+                                smallestSupportedJpegSize, smallestBoundJpegSize));
+            }
+
+            return smallestSupportedJpegSize;
+        }
+
+        return null;
+    }
+
+    private static Size findLargestByArea(List<Size> sizes) {
+        return Collections.max(sizes, new SizeAreaComparator());
+    }
+
+    private static boolean checkAspectRatiosMatch(Size a, Size b) {
+        float aAspect = a.getWidth() / (float) a.getHeight();
+        float bAspect = b.getWidth() / (float) b.getHeight();
+
+        return Math.abs(aAspect - bAspect) < ASPECT_RATIO_TOLERANCE;
+    }
+
+    private static List<Size> convertSizeList(List<Camera.Size> sizeList) {
+        List<Size> sizes = new ArrayList<>(sizeList.size());
+        for (Camera.Size s : sizeList) {
+            sizes.add(new Size(s.width, s.height));
+        }
+        return sizes;
     }
 
     // Calculate the highest FPS range supported
@@ -312,7 +544,6 @@
 
     private final Handler.Callback mRequestHandlerCb = new Handler.Callback() {
         private boolean mCleanup = false;
-        private List<RequestHolder> mRepeating = null;
 
         @SuppressWarnings("unchecked")
         @Override
@@ -321,10 +552,14 @@
                 return true;
             }
 
+            if (DEBUG) {
+                Log.d(TAG, "Request thread handling message:" + msg.what);
+            }
             switch (msg.what) {
                 case MSG_CONFIGURE_OUTPUTS:
                     ConfigureHolder config = (ConfigureHolder) msg.obj;
-                    Log.i(TAG, "Configure outputs: " + config.surfaces.size() +
+                    int sizes = config.surfaces != null ? config.surfaces.size() : 0;
+                    Log.i(TAG, "Configure outputs: " + sizes +
                             " surfaces configured.");
                     try {
                         configureOutputs(config.surfaces);
@@ -352,7 +587,15 @@
                     List<RequestHolder> requests =
                             nextBurst.first.produceRequestHolders(nextBurst.second);
                     for (RequestHolder holder : requests) {
+                        CaptureRequest request = holder.getRequest();
+                        if (mLastRequest == null || mLastRequest != request) {
+                            mLastRequest = request;
+                            LegacyMetadataMapper.convertRequestMetadata(mLastRequest,
+                                /*out*/mParams);
+                            mCamera.setParameters(mParams);
+                        }
                         mDeviceState.setCaptureStart(holder);
+                        long timestamp = 0;
                         try {
                             if (holder.hasPreviewTargets()) {
                                 mReceivedPreview.close();
@@ -361,23 +604,31 @@
                                     // TODO: report error to CameraDevice
                                     Log.e(TAG, "Hit timeout for preview callback!");
                                 }
+                                timestamp = mLastPreviewTimestamp;
                             }
                             if (holder.hasJpegTargets()) {
                                 mReceivedJpeg.close();
                                 doJpegCapture(holder);
-                                mReceivedJpeg.block();
                                 if (!mReceivedJpeg.block(JPEG_FRAME_TIMEOUT)) {
                                     // TODO: report error to CameraDevice
                                     Log.e(TAG, "Hit timeout for jpeg callback!");
                                 }
                                 mInFlightJpeg = null;
+                                timestamp = mLastJpegTimestamp;
                             }
                         } catch (IOException e) {
                             // TODO: err handling
                             throw new IOError(e);
                         }
-                        // TODO: Set fields in result.
-                        mDeviceState.setCaptureResult(holder, new CameraMetadataNative());
+                        if (timestamp == 0) {
+                            timestamp = SystemClock.elapsedRealtimeNanos();
+                        }
+                        CameraMetadataNative result = LegacyMetadataMapper.convertResultMetadata(mParams,
+                                request, timestamp);
+                        mDeviceState.setCaptureResult(holder, result);
+                    }
+                    if (DEBUG) {
+                        mRequestCounter.countAndLog();
                     }
                     break;
                 case MSG_CLEANUP:
@@ -437,6 +688,12 @@
         Handler handler = mRequestThread.waitAndGetHandler();
         handler.sendMessageAtFrontOfQueue(handler.obtainMessage(MSG_CLEANUP));
         mRequestThread.quitSafely();
+        try {
+            mRequestThread.join();
+        } catch (InterruptedException e) {
+            Log.e(TAG, String.format("Thread %s (%d) interrupted while quitting.",
+                    mRequestThread.getName(), mRequestThread.getId()));
+        }
     }
 
     /**
@@ -473,12 +730,14 @@
 
 
     /**
-     * Configure with the current output Surfaces.
+     * Configure with the current list of output Surfaces.
      *
      * <p>
      * This operation blocks until the configuration is complete.
      * </p>
      *
+     * <p>Using a {@code null} or empty {@code outputs} list is the equivalent of unconfiguring.</p>
+     *
      * @param outputs a {@link java.util.Collection} of outputs to configure.
      */
     public void configure(Collection<Surface> outputs) {
diff --git a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
index 2f0f6bc..bbc7005 100644
--- a/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
+++ b/core/java/android/hardware/camera2/legacy/SurfaceTextureRenderer.java
@@ -431,10 +431,15 @@
     public void configureSurfaces(Collection<Surface> surfaces) {
         releaseEGLContext();
 
+        if (surfaces == null || surfaces.size() == 0) {
+            Log.w(TAG, "No output surfaces configured for GL drawing.");
+            return;
+        }
+
         for (Surface s : surfaces) {
             // If pixel conversions aren't handled by egl, use a pbuffer
             if (LegacyCameraDevice.needsConversion(s)) {
-                LegacyCameraDevice.nativeSetSurfaceFormat(s, ImageFormat.NV21);
+                LegacyCameraDevice.nativeSetSurfaceFormat(s, ImageFormat.YV12);
                 EGLSurfaceHolder holder = new EGLSurfaceHolder();
                 holder.surface = s;
                 mConversionSurfaces.add(holder);
@@ -481,6 +486,7 @@
         }
         checkGlError("before updateTexImage");
         mSurfaceTexture.updateTexImage();
+        if (targetSurfaces == null) return;
         for (EGLSurfaceHolder holder : mSurfaces) {
             if (targetSurfaces.contains(holder.surface)) {
                 makeCurrent(holder.eglSurface);
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 3036425..fff171b 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -63,6 +63,12 @@
 public final class StreamConfigurationMap {
 
     private static final String TAG = "StreamConfigurationMap";
+
+    /**
+     * Indicates that a minimum frame duration is not available for a particular configuration.
+     */
+    public static final long NO_MIN_FRAME_DURATION = 0;
+
     /**
      * Create a new {@link StreamConfigurationMap}.
      *
@@ -359,7 +365,9 @@
      *
      * @param format an image format from {@link ImageFormat} or {@link PixelFormat}
      * @param size an output-compatible size
-     * @return a minimum frame duration {@code >=} 0 in nanoseconds
+     * @return a minimum frame duration {@code >} 0 in nanoseconds, or
+     *          {@link #NO_MIN_FRAME_DURATION} if the minimum frame duration is not available (this
+     *          can only occur on limited mode devices).
      *
      * @throws IllegalArgumentException if {@code format} or {@code size} was not supported
      * @throws NullPointerException if {@code size} was {@code null}
@@ -406,7 +414,9 @@
      *          a class which is supported by {@link #isOutputSupportedFor(Class)} and has a
      *          non-empty array returned by {@link #getOutputSizes(Class)}
      * @param size an output-compatible size
-     * @return a minimum frame duration {@code >=} 0 in nanoseconds
+     * @return a minimum frame duration {@code >} 0 in nanoseconds, or
+     *          {@link #NO_MIN_FRAME_DURATION} if the minimum frame duration is not available (this
+     *          can only occur on limited mode devices).
      *
      * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported
      * @throws NullPointerException if {@code size} or {@code klass} was {@code null}
@@ -892,7 +902,7 @@
     private long getDurationDefault(int duration) {
         switch (duration) {
             case DURATION_MIN_FRAME:
-                throw new AssertionError("Minimum frame durations are required to be listed");
+                return NO_MIN_FRAME_DURATION;
             case DURATION_STALL:
                 return 0L; // OK. A lack of a stall duration implies a 0 stall duration
             default:
diff --git a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
index 40cda08..898c746 100644
--- a/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
+++ b/core/java/android/hardware/camera2/utils/CameraBinderDecorator.java
@@ -47,6 +47,8 @@
      * - POLICY_PROHIBITS
      * - RESOURCE_BUSY
      * - NO_SUCH_DEVICE
+     * - NOT_SUPPORTED
+     * - TOO_MANY_USERS
      */
     public static final int EACCES = -13;
     public static final int EBUSY = -16;
diff --git a/core/java/android/hardware/camera2/utils/CloseableLock.java b/core/java/android/hardware/camera2/utils/CloseableLock.java
new file mode 100644
index 0000000..af55055
--- /dev/null
+++ b/core/java/android/hardware/camera2/utils/CloseableLock.java
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2014 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.camera2.utils;
+
+import android.util.Log;
+
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.ReentrantLock;
+
+/**
+ * Implement a shared/exclusive lock that can be closed.
+ *
+ * <p>A shared lock can be acquired if any other shared locks are also acquired. An
+ * exclusive lock acquire will block until all shared locks have been released.</p>
+ *
+ * <p>Locks are re-entrant; trying to acquire another lock (of the same type)
+ * while a lock is already held will immediately succeed.</p>
+ *
+ * <p>Acquiring to acquire a shared lock while holding an exclusive lock or vice versa is not
+ * supported; attempting it will throw an {@link IllegalStateException}.</p>
+ *
+ * <p>If the lock is closed, all future and current acquires will immediately return {@code null}.
+ * </p>
+ */
+public class CloseableLock implements AutoCloseable {
+
+    private static final boolean VERBOSE = false;
+
+    private final String TAG = "CloseableLock";
+    private final String mName;
+
+    private volatile boolean mClosed = false;
+
+    /** If an exclusive lock is acquired by some thread. */
+    private boolean mExclusive = false;
+    /**
+     * How many shared locks are acquired by any thread:
+     *
+     * <p>Reentrant locking increments this. If an exclusive lock is held,
+     * this value will stay at 0.</p>
+     */
+    private int mSharedLocks = 0;
+
+    private final ReentrantLock mLock = new ReentrantLock();
+    /** This condition automatically releases mLock when waiting; re-acquiring it after notify */
+    private final Condition mCondition = mLock.newCondition();
+
+    /** How many times the current thread is holding the lock */
+    private final ThreadLocal<Integer> mLockCount =
+        new ThreadLocal<Integer>() {
+            @Override protected Integer initialValue() {
+                return 0;
+            }
+        };
+
+    /**
+     * Helper class to release a lock at the end of a try-with-resources statement.
+     */
+    public class ScopedLock implements AutoCloseable {
+        private ScopedLock() {}
+
+        /** Release the lock with {@link CloseableLock#releaseLock}. */
+        @Override
+        public void close() {
+            releaseLock();
+        }
+    }
+
+    /**
+     * Create a new instance; starts out with 0 locks acquired.
+     */
+    public CloseableLock() {
+        mName = "";
+    }
+
+    /**
+     * Create a new instance; starts out with 0 locks acquired.
+     *
+     * @param name set an optional name for logging functionality
+     */
+    public CloseableLock(String name) {
+        mName = name;
+    }
+
+    /**
+     * Acquires the lock exclusively (blocking), marks it as closed, then releases the lock.
+     *
+     * <p>Marking a lock as closed will fail all further acquisition attempts;
+     * it will also immediately unblock all other threads currently trying to acquire a lock.</p>
+     *
+     * <p>This operation is idempotent; calling it more than once has no effect.</p>
+     *
+     * @throws IllegalStateException
+     *          if an attempt is made to {@code close} while this thread has a lock acquired
+     */
+    @Override
+    public void close() {
+        if (mClosed) {
+            log("close - already closed; ignoring");
+            return;
+        }
+
+        ScopedLock scoper = acquireExclusiveLock();
+        // Already closed by another thread?
+        if (scoper == null) {
+            return;
+        } else if (mLockCount.get() != 1) {
+            // Future: may want to add a #releaseAndClose to allow this.
+            throw new IllegalStateException(
+                    "Cannot close while one or more acquired locks are being held by this " +
+                     "thread; release all other locks first");
+        }
+
+        try {
+            mLock.lock();
+
+            mClosed = true;
+            mExclusive = false;
+            mSharedLocks = 0;
+            mLockCount.remove();
+
+            // Notify all threads that are waiting to unblock and return immediately
+            mCondition.signalAll();
+        } finally {
+            mLock.unlock();
+        }
+
+        log("close - completed");
+    }
+
+    /**
+     * Try to acquire the lock non-exclusively, blocking until the operation completes.
+     *
+     * <p>If the lock has already been closed, or being closed before this operation returns,
+     * the call will immediately return {@code false}.</p>
+     *
+     * <p>If other threads hold a non-exclusive lock (and the lock is not yet closed),
+     * this operation will return immediately. If another thread holds an exclusive lock,
+     * this thread will block until the exclusive lock has been released.</p>
+     *
+     * <p>This lock is re-entrant; acquiring more than one non-exclusive lock per thread is
+     * supported, and must be matched by an equal number of {@link #releaseLock} calls.</p>
+     *
+     * @return {@code ScopedLock} instance if the lock was acquired, or {@code null} if the lock
+     *         was already closed.
+     *
+     * @throws IllegalStateException if this thread is already holding an exclusive lock
+     */
+    public ScopedLock acquireLock() {
+
+        int ownedLocks;
+
+        try {
+            mLock.lock();
+
+            // Lock is already closed, all further acquisitions will fail
+            if (mClosed) {
+                log("acquire lock early aborted (already closed)");
+                return null;
+            }
+
+            ownedLocks = mLockCount.get();
+
+            // This thread is already holding an exclusive lock
+            if (mExclusive && ownedLocks > 0) {
+                throw new IllegalStateException(
+                        "Cannot acquire shared lock while holding exclusive lock");
+            }
+
+            // Is another thread holding the exclusive lock? Block until we can get in.
+            while (mExclusive) {
+                mCondition.awaitUninterruptibly();
+
+                // Did another thread #close while we were waiting? Unblock immediately.
+                if (mClosed) {
+                    log("acquire lock unblocked aborted (already closed)");
+                    return null;
+                }
+            }
+
+            mSharedLocks++;
+
+            ownedLocks = mLockCount.get() + 1;
+            mLockCount.set(ownedLocks);
+        } finally {
+            mLock.unlock();
+        }
+
+        log("acquired lock (local own count = " + ownedLocks + "");
+        return new ScopedLock();
+    }
+
+    /**
+     * Try to acquire the lock exclusively, blocking until all other threads release their locks.
+     *
+     * <p>If the lock has already been closed, or being closed before this operation returns,
+     * the call will immediately return {@code false}.</p>
+     *
+     * <p>If any other threads are holding a lock, this thread will block until all
+     * other locks are released.</p>
+     *
+     * <p>This lock is re-entrant; acquiring more than one exclusive lock per thread is supported,
+     * and must be matched by an equal number of {@link #releaseLock} calls.</p>
+     *
+     * @return {@code ScopedLock} instance if the lock was acquired, or {@code null} if the lock
+     *         was already closed.
+     *
+     * @throws IllegalStateException
+     *          if an attempt is made to acquire an exclusive lock while already holding a lock
+     */
+    public ScopedLock acquireExclusiveLock() {
+
+        int ownedLocks;
+
+        try {
+            mLock.lock();
+
+            // Lock is already closed, all further acquisitions will fail
+            if (mClosed) {
+                log("acquire exclusive lock early aborted (already closed)");
+                return null;
+            }
+
+            ownedLocks = mLockCount.get();
+
+            // This thread is already holding a shared lock
+            if (!mExclusive && ownedLocks > 0) {
+                throw new IllegalStateException(
+                        "Cannot acquire exclusive lock while holding shared lock");
+            }
+
+            /*
+             * Is another thread holding the lock? Block until we can get in.
+             *
+             * If we are already holding the lock, always let it through since
+             * we are just reentering the exclusive lock.
+             */
+            while (ownedLocks == 0 && (mExclusive || mSharedLocks > 0)) {
+                mCondition.awaitUninterruptibly();
+
+             // Did another thread #close while we were waiting? Unblock immediately.
+                if (mClosed) {
+                    log("acquire exclusive lock unblocked aborted (already closed)");
+                    return null;
+                }
+            }
+
+            mExclusive = true;
+
+            ownedLocks = mLockCount.get() + 1;
+            mLockCount.set(ownedLocks);
+        } finally {
+            mLock.unlock();
+        }
+
+        log("acquired exclusive lock (local own count = " + ownedLocks + "");
+        return new ScopedLock();
+    }
+
+    /**
+     * Release a single lock that was acquired.
+     *
+     * <p>Any other other that is blocked and trying to acquire a lock will get a chance
+     * to acquire the lock.</p>
+     *
+     * @throws IllegalStateException if no locks were acquired, or if the lock was already closed
+     */
+    public void releaseLock() {
+        if (mLockCount.get() <= 0) {
+            throw new IllegalStateException(
+                    "Cannot release lock that was not acquired by this thread");
+        }
+
+        int ownedLocks;
+
+        try {
+            mLock.lock();
+
+            // Lock is already closed, it couldn't have been acquired in the first place
+            if (mClosed) {
+                throw new IllegalStateException("Do not release after the lock has been closed");
+            }
+
+            if (!mExclusive) {
+                mSharedLocks--;
+            } else {
+                if (mSharedLocks != 0) {
+                    throw new AssertionError("Too many shared locks " + mSharedLocks);
+                }
+            }
+
+            ownedLocks = mLockCount.get() - 1;
+            mLockCount.set(ownedLocks);
+
+            if (ownedLocks == 0 && mExclusive) {
+                // Wake up any threads that might be waiting for the exclusive lock to be released
+                mExclusive = false;
+                mCondition.signalAll();
+            } else if (ownedLocks == 0 && mSharedLocks == 0) {
+                // Wake up any threads that might be trying to get the exclusive lock
+                mCondition.signalAll();
+            }
+        } finally {
+            mLock.unlock();
+        }
+
+        log("released lock (local lock count " + ownedLocks + ")");
+    }
+
+    private void log(String what) {
+        if (VERBOSE) {
+            Log.v(TAG + "[" + mName + "]", what);
+        }
+    }
+
+}
diff --git a/core/java/android/hardware/hdmi/HdmiCec.java b/core/java/android/hardware/hdmi/HdmiCec.java
index 8ad9463..d86dd5e 100644
--- a/core/java/android/hardware/hdmi/HdmiCec.java
+++ b/core/java/android/hardware/hdmi/HdmiCec.java
@@ -185,6 +185,7 @@
     public static final int RESULT_TARGET_NOT_AVAILABLE = 3;
     public static final int RESULT_ALREADY_IN_PROGRESS = 4;
     public static final int RESULT_EXCEPTION = 5;
+    public static final int RESULT_INCORRECT_MODE = 6;
 
     private static final int[] ADDRESS_TO_TYPE = {
         DEVICE_TV,  // ADDR_TV
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 559a469..73726fa 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -21,6 +21,7 @@
 import android.hardware.hdmi.IHdmiControlCallback;
 import android.hardware.hdmi.IHdmiDeviceEventListener;
 import android.hardware.hdmi.IHdmiHotplugEventListener;
+import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
 
 import java.util.List;
 
@@ -42,4 +43,9 @@
     void portSelect(int portId, IHdmiControlCallback callback);
     void sendKeyEvent(int keyCode, boolean isPressed);
     List<HdmiPortInfo> getPortInfo();
+    boolean canChangeSystemAudioMode();
+    boolean getSystemAudioMode();
+    void setSystemAudioMode(boolean enabled, IHdmiControlCallback callback);
+    void addSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
+    void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener);
 }
diff --git a/core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl b/core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl
new file mode 100644
index 0000000..714bbe7
--- /dev/null
+++ b/core/java/android/hardware/hdmi/IHdmiSystemAudioModeChangeListener.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 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.hdmi;
+
+/**
+ * Callback interface definition for HDMI client to get informed of
+ * "System Audio" mode change.
+ *
+ * @hide
+ */
+oneway interface IHdmiSystemAudioModeChangeListener {
+
+    /**
+     * @param enabled true if the device gets activated
+     */
+    void onStatusChanged(in boolean enabled);
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index ba31243..b68ce36 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -68,6 +68,7 @@
  */
 public class ConnectivityManager {
     private static final String TAG = "ConnectivityManager";
+    private static final boolean LEGACY_DBG = true; // STOPSHIP
 
     /**
      * A change in network connectivity has occurred. A default connection has either
@@ -823,6 +824,14 @@
 
         NetworkRequest request = null;
         synchronized (sLegacyRequests) {
+            if (LEGACY_DBG) {
+                Log.d(TAG, "Looking for legacyRequest for netCap with hash: " + netCap + " (" +
+                        netCap.hashCode() + ")");
+                Log.d(TAG, "sLegacyRequests has:");
+                for (NetworkCapabilities nc : sLegacyRequests.keySet()) {
+                    Log.d(TAG, "  " + nc + " (" + nc.hashCode() + ")");
+                }
+            }
             LegacyRequest l = sLegacyRequests.get(netCap);
             if (l != null) {
                 Log.d(TAG, "renewing startUsingNetworkFeature request " + l.networkRequest);
@@ -837,7 +846,7 @@
             request = requestNetworkForFeatureLocked(netCap);
         }
         if (request != null) {
-            Log.d(TAG, "starting startUsingNeworkFeature for request " + request);
+            Log.d(TAG, "starting startUsingNetworkFeature for request " + request);
             return PhoneConstants.APN_REQUEST_STARTED;
         } else {
             Log.d(TAG, " request Failed");
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 535bbe2..8142670 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -699,15 +699,15 @@
                 break;
             }
 
-            try {
-                if (enable) {
-                    return mPhoneService.enableApnType(apnType);
-                } else {
-                    return mPhoneService.disableApnType(apnType);
-                }
-            } catch (RemoteException e) {
-                if (retry == 0) getPhoneService(true);
-            }
+//            try {
+//                if (enable) {
+//                    return mPhoneService.enableApnType(apnType);
+//                } else {
+//                    return mPhoneService.disableApnType(apnType);
+//                }
+//            } catch (RemoteException e) {
+//                if (retry == 0) getPhoneService(true);
+//            }
         }
 
         loge("Could not " + (enable ? "enable" : "disable") + " APN type \"" + apnType + "\"");
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 318aabe..0a422c6 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -190,13 +190,20 @@
             }
     };
 
+    @Override
     public boolean equals(Object obj) {
         if (obj instanceof Network == false) return false;
         Network other = (Network)obj;
         return this.netId == other.netId;
     }
 
+    @Override
     public int hashCode() {
         return netId * 11;
     }
+
+    @Override
+    public String toString() {
+        return Integer.toString(netId);
+    }
 }
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 9218c11..53e87a6 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -25,6 +25,7 @@
 import android.nfc.INfcAdapterExtras;
 import android.nfc.INfcTag;
 import android.nfc.INfcCardEmulation;
+import android.nfc.INfcLockscreenDispatch;
 import android.os.Bundle;
 
 /**
@@ -52,4 +53,6 @@
 
     void setReaderMode (IBinder b, IAppCallback callback, int flags, in Bundle extras);
     void setP2pModes(int initatorModes, int targetModes);
+
+    void registerLockscreenDispatch(INfcLockscreenDispatch lockscreenDispatch, in int[] techList);
 }
diff --git a/core/java/android/nfc/INfcLockscreenDispatch.aidl b/core/java/android/nfc/INfcLockscreenDispatch.aidl
new file mode 100644
index 0000000..27e9a8c
--- /dev/null
+++ b/core/java/android/nfc/INfcLockscreenDispatch.aidl
@@ -0,0 +1,12 @@
+package android.nfc;
+
+import android.nfc.Tag;
+
+/**
+ * @hide
+ */
+interface INfcLockscreenDispatch {
+
+    boolean onTagDetected(in Tag tag);
+
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index dd8e41c..be098a8 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -380,6 +380,16 @@
         public Uri[] createBeamUris(NfcEvent event);
     }
 
+
+    /**
+     * A callback to be invoked when an application has registered for receiving
+     * tags at the lockscreen.
+     */
+    public interface NfcLockscreenDispatch {
+        public boolean onTagDetected(Tag tag);
+    }
+
+
     /**
      * Helper to check if this device has FEATURE_NFC, but without using
      * a context.
@@ -1417,6 +1427,26 @@
         }
     }
 
+    public boolean registerLockscreenDispatch(final NfcLockscreenDispatch lockscreenDispatch,
+                                           String[] techList) {
+        try {
+            sService.registerLockscreenDispatch(new INfcLockscreenDispatch.Stub() {
+                @Override
+                public boolean onTagDetected(Tag tag) throws RemoteException {
+                    return lockscreenDispatch.onTagDetected(tag);
+                }
+            }, Tag.techListFromStrings(techList));
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+            return false;
+        } catch (IllegalArgumentException e) {
+            Log.e(TAG, "Unable to register LockscreenDispatch", e);
+            return false;
+        }
+
+        return true;
+    }
+
     /**
      * @hide
      */
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index 0d261d1..43be702 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -35,6 +35,7 @@
 
 import java.io.IOException;
 import java.util.Arrays;
+import java.util.HashMap;
 
 /**
  * Represents an NFC tag that has been discovered.
@@ -195,6 +196,41 @@
         return strings;
     }
 
+    static int[] techListFromStrings(String[] techStringList) throws IllegalArgumentException {
+        if (techStringList == null) {
+            throw new IllegalArgumentException("List cannot be null");
+        }
+        int[] techIntList = new int[techStringList.length];
+        HashMap<String, Integer> stringToCodeMap = getTechStringToCodeMap();
+        for (int i = 0; i < techStringList.length; i++) {
+            Integer code = stringToCodeMap.get(techStringList[i]);
+
+            if (code == null) {
+                throw new IllegalArgumentException("Unknown tech type " + techStringList[i]);
+            }
+
+            techIntList[i] = code.intValue();
+        }
+        return techIntList;
+    }
+
+    private static HashMap<String, Integer> getTechStringToCodeMap() {
+        HashMap<String, Integer> techStringToCodeMap = new HashMap<String, Integer>();
+
+        techStringToCodeMap.put(IsoDep.class.getName(), TagTechnology.ISO_DEP);
+        techStringToCodeMap.put(MifareClassic.class.getName(), TagTechnology.MIFARE_CLASSIC);
+        techStringToCodeMap.put(MifareUltralight.class.getName(), TagTechnology.MIFARE_ULTRALIGHT);
+        techStringToCodeMap.put(Ndef.class.getName(), TagTechnology.NDEF);
+        techStringToCodeMap.put(NdefFormatable.class.getName(), TagTechnology.NDEF_FORMATABLE);
+        techStringToCodeMap.put(NfcA.class.getName(), TagTechnology.NFC_A);
+        techStringToCodeMap.put(NfcB.class.getName(), TagTechnology.NFC_B);
+        techStringToCodeMap.put(NfcF.class.getName(), TagTechnology.NFC_F);
+        techStringToCodeMap.put(NfcV.class.getName(), TagTechnology.NFC_V);
+        techStringToCodeMap.put(NfcBarcode.class.getName(), TagTechnology.NFC_BARCODE);
+
+        return techStringToCodeMap;
+    }
+
     /**
      * For use by NfcService only.
      * @hide
diff --git a/core/java/android/os/BatteryManagerInternal.java b/core/java/android/os/BatteryManagerInternal.java
new file mode 100644
index 0000000..f3a95b9
--- /dev/null
+++ b/core/java/android/os/BatteryManagerInternal.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 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.os;
+
+/**
+ * Battery manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class BatteryManagerInternal {
+    /**
+     * Returns true if the device is plugged into any of the specified plug types.
+     */
+    public abstract boolean isPowered(int plugTypeSet);
+
+    /**
+     * Returns the current plug type.
+     */
+    public abstract int getPlugType();
+
+    /**
+     * Returns battery level as a percentage.
+     */
+    public abstract int getBatteryLevel();
+
+    /**
+     * Returns whether we currently consider the battery level to be low.
+     */
+    public abstract boolean getBatteryLevelLow();
+
+    /**
+     * Returns a non-zero value if an unsupported charger is attached.
+     */
+    public abstract int getInvalidCharger();
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index e627d49..f7d2bfd 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -171,6 +171,10 @@
     private static final String DATA_CONNECTION_COUNT_DATA = "dcc";
     private static final String WIFI_STATE_TIME_DATA = "wst";
     private static final String WIFI_STATE_COUNT_DATA = "wsc";
+    private static final String WIFI_SUPPL_STATE_TIME_DATA = "wsst";
+    private static final String WIFI_SUPPL_STATE_COUNT_DATA = "wssc";
+    private static final String WIFI_SIGNAL_STRENGTH_TIME_DATA = "wsgt";
+    private static final String WIFI_SIGNAL_STRENGTH_COUNT_DATA = "wsgc";
     private static final String BLUETOOTH_STATE_TIME_DATA = "bst";
     private static final String BLUETOOTH_STATE_COUNT_DATA = "bsc";
     private static final String POWER_USE_SUMMARY_DATA = "pws";
@@ -563,8 +567,8 @@
         public static final int STATE_BRIGHTNESS_SHIFT = 0;
         public static final int STATE_BRIGHTNESS_MASK = 0x7;
         // Constants from SIGNAL_STRENGTH_*
-        public static final int STATE_SIGNAL_STRENGTH_SHIFT = 3;
-        public static final int STATE_SIGNAL_STRENGTH_MASK = 0x7 << STATE_SIGNAL_STRENGTH_SHIFT;
+        public static final int STATE_PHONE_SIGNAL_STRENGTH_SHIFT = 3;
+        public static final int STATE_PHONE_SIGNAL_STRENGTH_MASK = 0x7 << STATE_PHONE_SIGNAL_STRENGTH_SHIFT;
         // Constants from ServiceState.STATE_*
         public static final int STATE_PHONE_STATE_SHIFT = 6;
         public static final int STATE_PHONE_STATE_MASK = 0x7 << STATE_PHONE_STATE_SHIFT;
@@ -582,7 +586,6 @@
         public static final int STATE_WIFI_SCAN_FLAG = 1<<27;
         public static final int STATE_WIFI_MULTICAST_ON_FLAG = 1<<26;
         public static final int STATE_MOBILE_RADIO_ACTIVE_FLAG = 1<<25;
-        public static final int STATE_WIFI_RUNNING_FLAG = 1<<24;
         // These are on the lower bits used for the command; if they change
         // we need to write another int of data.
         public static final int STATE_SENSOR_ON_FLAG = 1<<23;
@@ -591,17 +594,30 @@
         public static final int STATE_SCREEN_ON_FLAG = 1<<20;
         public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19;
         public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18;
-        public static final int STATE_WIFI_ON_FLAG = 1<<17;
         public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16;
 
         public static final int MOST_INTERESTING_STATES =
             STATE_BATTERY_PLUGGED_FLAG | STATE_SCREEN_ON_FLAG
-            | STATE_GPS_ON_FLAG | STATE_PHONE_IN_CALL_FLAG;
+            | STATE_PHONE_IN_CALL_FLAG | STATE_BLUETOOTH_ON_FLAG;
 
         public int states;
 
-        public static final int STATE2_VIDEO_ON_FLAG = 1<<0;
-        public static final int STATE2_LOW_POWER_FLAG = 1<<1;
+        // Constants from WIFI_SUPPL_STATE_*
+        public static final int STATE2_WIFI_SUPPL_STATE_SHIFT = 0;
+        public static final int STATE2_WIFI_SUPPL_STATE_MASK = 0xf;
+        // Values for NUM_WIFI_SIGNAL_STRENGTH_BINS
+        public static final int STATE2_WIFI_SIGNAL_STRENGTH_SHIFT = 4;
+        public static final int STATE2_WIFI_SIGNAL_STRENGTH_MASK =
+                0x7 << STATE2_WIFI_SIGNAL_STRENGTH_SHIFT;
+
+        public static final int STATE2_LOW_POWER_FLAG = 1<<31;
+        public static final int STATE2_VIDEO_ON_FLAG = 1<<30;
+        public static final int STATE2_WIFI_RUNNING_FLAG = 1<<29;
+        public static final int STATE2_WIFI_ON_FLAG = 1<<28;
+
+        public static final int MOST_INTERESTING_STATES2 =
+            STATE2_LOW_POWER_FLAG | STATE2_WIFI_ON_FLAG;
+
         public int states2;
 
         // The wake lock that was acquired at this point.
@@ -1005,7 +1021,7 @@
     };
 
     public static final int NUM_SCREEN_BRIGHTNESS_BINS = 5;
-    
+
     /**
      * Returns the time in microseconds that the screen has been on with
      * the given brightness
@@ -1153,6 +1169,34 @@
      */
     public abstract int getPhoneDataConnectionCount(int dataType, int which);
 
+    public static final int WIFI_SUPPL_STATE_INVALID = 0;
+    public static final int WIFI_SUPPL_STATE_DISCONNECTED = 1;
+    public static final int WIFI_SUPPL_STATE_INTERFACE_DISABLED = 2;
+    public static final int WIFI_SUPPL_STATE_INACTIVE = 3;
+    public static final int WIFI_SUPPL_STATE_SCANNING = 4;
+    public static final int WIFI_SUPPL_STATE_AUTHENTICATING = 5;
+    public static final int WIFI_SUPPL_STATE_ASSOCIATING = 6;
+    public static final int WIFI_SUPPL_STATE_ASSOCIATED = 7;
+    public static final int WIFI_SUPPL_STATE_FOUR_WAY_HANDSHAKE = 8;
+    public static final int WIFI_SUPPL_STATE_GROUP_HANDSHAKE = 9;
+    public static final int WIFI_SUPPL_STATE_COMPLETED = 10;
+    public static final int WIFI_SUPPL_STATE_DORMANT = 11;
+    public static final int WIFI_SUPPL_STATE_UNINITIALIZED = 12;
+
+    public static final int NUM_WIFI_SUPPL_STATES = WIFI_SUPPL_STATE_UNINITIALIZED+1;
+
+    static final String[] WIFI_SUPPL_STATE_NAMES = {
+        "invalid", "disconn", "disabled", "inactive", "scanning",
+        "authenticating", "associating", "associated", "4-way-handshake",
+        "group-handshake", "completed", "dormant", "uninit"
+    };
+
+    static final String[] WIFI_SUPPL_STATE_SHORT_NAMES = {
+        "inv", "dsc", "dis", "inact", "scan",
+        "auth", "ascing", "asced", "4-way",
+        "group", "compl", "dorm", "uninit"
+    };
+
     public static final BitDescription[] HISTORY_STATE_DESCRIPTIONS
             = new BitDescription[] {
         new BitDescription(HistoryItem.STATE_CPU_RUNNING_FLAG, "running", "r"),
@@ -1163,13 +1207,11 @@
         new BitDescription(HistoryItem.STATE_WIFI_SCAN_FLAG, "wifi_scan", "Ws"),
         new BitDescription(HistoryItem.STATE_WIFI_MULTICAST_ON_FLAG, "wifi_multicast", "Wm"),
         new BitDescription(HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG, "mobile_radio", "Pr"),
-        new BitDescription(HistoryItem.STATE_WIFI_RUNNING_FLAG, "wifi_running", "Wr"),
         new BitDescription(HistoryItem.STATE_PHONE_SCANNING_FLAG, "phone_scanning", "Psc"),
         new BitDescription(HistoryItem.STATE_AUDIO_ON_FLAG, "audio", "a"),
         new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"),
         new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"),
         new BitDescription(HistoryItem.STATE_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"),
-        new BitDescription(HistoryItem.STATE_WIFI_ON_FLAG, "wifi", "W"),
         new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth", "b"),
         new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK,
                 HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn", "Pcn",
@@ -1178,11 +1220,10 @@
                 HistoryItem.STATE_PHONE_STATE_SHIFT, "phone_state", "Pst",
                 new String[] {"in", "out", "emergency", "off"},
                 new String[] {"in", "out", "em", "off"}),
-        new BitDescription(HistoryItem.STATE_SIGNAL_STRENGTH_MASK,
-                HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT, "signal_strength", "Pss",
-                SignalStrength.SIGNAL_STRENGTH_NAMES, new String[] {
-                    "0", "1", "2", "3", "4"
-        }),
+        new BitDescription(HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK,
+                HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT, "phone_signal_strength", "Pss",
+                SignalStrength.SIGNAL_STRENGTH_NAMES,
+                new String[] { "0", "1", "2", "3", "4" }),
         new BitDescription(HistoryItem.STATE_BRIGHTNESS_MASK,
                 HistoryItem.STATE_BRIGHTNESS_SHIFT, "brightness", "Sb",
                 SCREEN_BRIGHTNESS_NAMES, SCREEN_BRIGHTNESS_SHORT_NAMES),
@@ -1190,8 +1231,17 @@
 
     public static final BitDescription[] HISTORY_STATE2_DESCRIPTIONS
             = new BitDescription[] {
-        new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
         new BitDescription(HistoryItem.STATE2_LOW_POWER_FLAG, "low_power", "lp"),
+        new BitDescription(HistoryItem.STATE2_VIDEO_ON_FLAG, "video", "v"),
+        new BitDescription(HistoryItem.STATE2_WIFI_RUNNING_FLAG, "wifi_running", "Wr"),
+        new BitDescription(HistoryItem.STATE2_WIFI_ON_FLAG, "wifi", "W"),
+        new BitDescription(HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK,
+                HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT, "wifi_signal_strength", "Wss",
+                new String[] { "0", "1", "2", "3", "4" },
+                new String[] { "0", "1", "2", "3", "4" }),
+        new BitDescription(HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK,
+                HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT, "wifi_suppl", "Wsp",
+                WIFI_SUPPL_STATE_NAMES, WIFI_SUPPL_STATE_SHORT_NAMES),
     };
 
     public static final String[] HISTORY_EVENT_NAMES = new String[] {
@@ -1250,6 +1300,40 @@
     public abstract int getWifiStateCount(int wifiState, int which);
 
     /**
+     * Returns the time in microseconds that the wifi supplicant has been
+     * in a given state.
+     *
+     * {@hide}
+     */
+    public abstract long getWifiSupplStateTime(int state, long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times that the wifi supplicant has transitioned
+     * to a given state.
+     *
+     * {@hide}
+     */
+    public abstract int getWifiSupplStateCount(int state, int which);
+
+    public static final int NUM_WIFI_SIGNAL_STRENGTH_BINS = 5;
+
+    /**
+     * Returns the time in microseconds that WIFI has been running with
+     * the given signal strength.
+     *
+     * {@hide}
+     */
+    public abstract long getWifiSignalStrengthTime(int strengthBin,
+            long elapsedRealtimeUs, int which);
+
+    /**
+     * Returns the number of times WIFI has entered the given signal strength.
+     *
+     * {@hide}
+     */
+    public abstract int getWifiSignalStrengthCount(int strengthBin, int which);
+
+    /**
      * Returns the time in microseconds that bluetooth has been on while the device was
      * running on battery.
      * 
@@ -1780,6 +1864,28 @@
         }
         dumpLine(pw, 0 /* uid */, category, WIFI_STATE_COUNT_DATA, args);
 
+        // Dump wifi suppl state stats
+        args = new Object[NUM_WIFI_SUPPL_STATES];
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            args[i] = getWifiSupplStateTime(i, rawRealtime, which) / 1000;
+        }
+        dumpLine(pw, 0 /* uid */, category, WIFI_SUPPL_STATE_TIME_DATA, args);
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            args[i] = getWifiSupplStateCount(i, which);
+        }
+        dumpLine(pw, 0 /* uid */, category, WIFI_SUPPL_STATE_COUNT_DATA, args);
+
+        // Dump wifi signal strength stats
+        args = new Object[NUM_WIFI_SIGNAL_STRENGTH_BINS];
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            args[i] = getWifiSignalStrengthTime(i, rawRealtime, which) / 1000;
+        }
+        dumpLine(pw, 0 /* uid */, category, WIFI_SIGNAL_STRENGTH_TIME_DATA, args);
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            args[i] = getWifiSignalStrengthCount(i, which);
+        }
+        dumpLine(pw, 0 /* uid */, category, WIFI_SIGNAL_STRENGTH_COUNT_DATA, args);
+
         // Dump bluetooth state stats
         args = new Object[NUM_BLUETOOTH_STATES];
         for (int i=0; i<NUM_BLUETOOTH_STATES; i++) {
@@ -2254,7 +2360,7 @@
                 pw.print(", sent "); pw.print(mobileTxTotalPackets); pw.println(")");
         sb.setLength(0);
         sb.append(prefix);
-        sb.append("  Signal levels:");
+        sb.append("  Phone signal levels:");
         didOne = false;
         for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             final long time = getPhoneSignalStrengthTime(i, rawRealtime, which);
@@ -2372,7 +2478,55 @@
             sb.append("(");
             sb.append(formatRatioLocked(time, whichBatteryRealtime));
             sb.append(") ");
-            sb.append(getPhoneDataConnectionCount(i, which));
+            sb.append(getWifiStateCount(i, which));
+            sb.append("x");
+        }
+        if (!didOne) sb.append(" (no activity)");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Wifi supplicant states:");
+        didOne = false;
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            final long time = getWifiSupplStateTime(i, rawRealtime, which);
+            if (time == 0) {
+                continue;
+            }
+            sb.append("\n    ");
+            didOne = true;
+            sb.append(WIFI_SUPPL_STATE_NAMES[i]);
+            sb.append(" ");
+            formatTimeMs(sb, time/1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(time, whichBatteryRealtime));
+            sb.append(") ");
+            sb.append(getWifiSupplStateCount(i, which));
+            sb.append("x");
+        }
+        if (!didOne) sb.append(" (no activity)");
+        pw.println(sb.toString());
+
+        sb.setLength(0);
+        sb.append(prefix);
+        sb.append("  Wifi signal levels:");
+        didOne = false;
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            final long time = getWifiSignalStrengthTime(i, rawRealtime, which);
+            if (time == 0) {
+                continue;
+            }
+            sb.append("\n    ");
+            sb.append(prefix);
+            didOne = true;
+            sb.append("level(");
+            sb.append(i);
+            sb.append(") ");
+            formatTimeMs(sb, time/1000);
+            sb.append("(");
+            sb.append(formatRatioLocked(time, whichBatteryRealtime));
+            sb.append(") ");
+            sb.append(getWifiSignalStrengthCount(i, which));
             sb.append("x");
         }
         if (!didOne) sb.append(" (no activity)");
@@ -3039,6 +3193,16 @@
         long lastTime = -1;
         long firstTime = -1;
 
+        void reset() {
+            oldState = oldState2 = 0;
+            oldLevel = -1;
+            oldStatus = -1;
+            oldHealth = -1;
+            oldPlug = -1;
+            oldTemp = -1;
+            oldVolt = -1;
+        }
+
         public void printNextItem(PrintWriter pw, HistoryItem rec, long baseTime, boolean checkin,
                 boolean verbose) {
             if (!checkin) {
@@ -3062,6 +3226,7 @@
                     pw.print(":");
                 }
                 pw.println("START");
+                reset();
             } else if (rec.cmd == HistoryItem.CMD_CURRENT_TIME
                     || rec.cmd == HistoryItem.CMD_RESET) {
                 if (checkin) {
@@ -3069,6 +3234,7 @@
                 }
                 if (rec.cmd == HistoryItem.CMD_RESET) {
                     pw.print("RESET:");
+                    reset();
                 }
                 pw.print("TIME:");
                 if (checkin) {
diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java
index e42c3fe..3252d19 100644
--- a/core/java/android/os/Bundle.java
+++ b/core/java/android/os/Bundle.java
@@ -692,10 +692,12 @@
 
     /**
      * Returns the value associated with the given key, or defaultValue if
-     * no mapping of the desired type exists for the given key.
+     * no mapping of the desired type exists for the given key or if a null
+     * value is explicitly associatd with the given key.
      *
      * @param key a String, or null
-     * @param defaultValue Value to return if key does not exist
+     * @param defaultValue Value to return if key does not exist or if a null
+     *     value is associated with the given key.
      * @return the CharSequence value associated with the given key, or defaultValue
      *     if no valid CharSequence object is currently mapped to that key.
      */
diff --git a/core/java/android/os/PowerManagerInternal.java b/core/java/android/os/PowerManagerInternal.java
index 69b828f..08a15eb 100644
--- a/core/java/android/os/PowerManagerInternal.java
+++ b/core/java/android/os/PowerManagerInternal.java
@@ -16,8 +16,6 @@
 
 package android.os;
 
-import android.view.WindowManagerPolicy;
-
 /**
  * Power manager local system service interface.
  *
@@ -57,12 +55,9 @@
 
     public abstract boolean getLowPowerModeEnabled();
 
+    public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
+
     public interface LowPowerModeListener {
         public void onLowPowerModeChanged(boolean enabled);
     }
-
-    public abstract void registerLowPowerModeObserver(LowPowerModeListener listener);
-
-    // TODO: Remove this and retrieve as a local service instead.
-    public abstract void setPolicy(WindowManagerPolicy policy);
 }
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 468cfe0..96db772 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -226,14 +226,14 @@
     public static final String DISALLOW_CONFIG_MOBILE_NETWORKS = "no_config_mobile_networks";
 
     /**
-     * Key for user restrictions. Specifies if a user is disallowed from configuring
+     * Key for user restrictions. Specifies if a user is disallowed from controlling
      * applications in Settings. The default value is <code>false</code>.
      * <p>
      * Type: Boolean
      * @see #setUserRestrictions(Bundle)
      * @see #getUserRestrictions()
      */
-    public static final String DISALLOW_CONFIG_APPS = "no_config_apps";
+    public static final String DISALLOW_APPS_CONTROL = "no_control_apps";
 
     /**
      * Key for user restrictions. Specifies if a user is disallowed from mounting
@@ -719,6 +719,26 @@
 
     /**
      * If the target user is a managed profile of the calling user or the caller
+     * is itself a managed profile, then this returns a copy of the label with
+     * badging for accessibility services like talkback. E.g. passing in "Email"
+     * and it might return "Work Email" for Email in the work profile.
+     *
+     * @param label The label to change.
+     * @param user The target user.
+     * @return A label that combines the original label and a badge as
+     *         determined by the system.
+     */
+    public String getBadgedLabelForUser(String label, UserHandle user) {
+        UserInfo userInfo = getUserIfProfile(user.getIdentifier());
+        if (userInfo != null && userInfo.isManagedProfile()) {
+            return Resources.getSystem().getString(
+                    R.string.managed_profile_label_badge, label);
+        }
+        return label;
+    }
+
+    /**
+     * If the target user is a managed profile of the calling user or the caller
      * is itself a managed profile, then this returns a drawable to use as a small
      * icon to include in a view to distinguish it from the original icon.
      *
diff --git a/core/java/android/preference/PreferenceActivity.java b/core/java/android/preference/PreferenceActivity.java
index 0418049..23b1e2c 100644
--- a/core/java/android/preference/PreferenceActivity.java
+++ b/core/java/android/preference/PreferenceActivity.java
@@ -1194,7 +1194,14 @@
      * @param args Optional arguments to supply to the fragment.
      */
     public void switchToHeader(String fragmentName, Bundle args) {
-        setSelectedHeader(null);
+        Header selectedHeader = null;
+        for (int i = 0; i < mHeaders.size(); i++) {
+            if (fragmentName.equals(mHeaders.get(i).fragment)) {
+                selectedHeader = mHeaders.get(i);
+                break;
+            }
+        }
+        setSelectedHeader(selectedHeader);
         switchToHeaderInner(fragmentName, args);
     }
 
diff --git a/core/java/android/preference/PreferenceGroupAdapter.java b/core/java/android/preference/PreferenceGroupAdapter.java
index 381a5f0..5a0b9e9 100644
--- a/core/java/android/preference/PreferenceGroupAdapter.java
+++ b/core/java/android/preference/PreferenceGroupAdapter.java
@@ -27,6 +27,7 @@
 import android.view.ViewGroup;
 import android.widget.Adapter;
 import android.widget.BaseAdapter;
+import android.widget.FrameLayout;
 import android.widget.ListView;
 
 /**
@@ -95,6 +96,9 @@
     private int mHighlightedPosition = -1;
     private Drawable mHighlightedDrawable;
 
+    private static ViewGroup.LayoutParams sWrapperLayoutParams = new ViewGroup.LayoutParams(
+            ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
+
     private static class PreferenceLayout implements Comparable<PreferenceLayout> {
         private int resId;
         private int widgetResId;
@@ -235,14 +239,18 @@
 
         // If it's not one of the cached ones, set the convertView to null so that 
         // the layout gets re-created by the Preference.
-        if (Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout) < 0) {
+        if (Collections.binarySearch(mPreferenceLayouts, mTempPreferenceLayout) < 0 ||
+                (getItemViewType(position) == getHighlightItemViewType())) {
             convertView = null;
         }
         View result = preference.getView(convertView, parent);
         if (position == mHighlightedPosition && mHighlightedDrawable != null) {
-            result.setBackgroundDrawable(mHighlightedDrawable);
+            ViewGroup wrapper = new FrameLayout(parent.getContext());
+            wrapper.setLayoutParams(sWrapperLayoutParams);
+            wrapper.setBackgroundDrawable(mHighlightedDrawable);
+            wrapper.addView(result);
+            result = wrapper;
         }
-        result.setTag(preference.getKey());
         return result;
     }
 
@@ -273,8 +281,16 @@
         return true;
     }
 
+    private int getHighlightItemViewType() {
+        return getViewTypeCount() - 1;
+    }
+
     @Override
     public int getItemViewType(int position) {
+        if (position == mHighlightedPosition) {
+            return getHighlightItemViewType();
+        }
+
         if (!mHasReturnedViewTypeCount) {
             mHasReturnedViewTypeCount = true;
         }
@@ -302,7 +318,7 @@
             mHasReturnedViewTypeCount = true;
         }
         
-        return Math.max(1, mPreferenceLayouts.size());
+        return Math.max(1, mPreferenceLayouts.size()) + 1;
     }
 
 }
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 1e7d7f1..0d69b3b 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -811,7 +811,6 @@
          * The position at which the contact is pinned. If {@link PinnedPositions#UNPINNED},
          * the contact is not pinned. Also see {@link PinnedPositions}.
          * <P>Type: INTEGER </P>
-         * @hide
          */
         public static final String PINNED = "pinned";
 
@@ -1694,7 +1693,7 @@
          */
         public static final class Entity implements BaseColumns, ContactsColumns,
                 ContactNameColumns, RawContactsColumns, BaseSyncColumns, SyncColumns, DataColumns,
-                StatusColumns, ContactOptionsColumns, ContactStatusColumns {
+                StatusColumns, ContactOptionsColumns, ContactStatusColumns, DataUsageStatColumns {
             /**
              * no public constructor since this is a utility class
              */
@@ -7745,8 +7744,8 @@
 
     /**
      * <p>
-     * API allowing applications to send pinning information for specified contacts to the
-     * Contacts Provider.
+     * Contact-specific information about whether or not a contact has been pinned by the user
+     * at a particular position within the system contact application's user interface.
      * </p>
      *
      * <p>
@@ -7771,46 +7770,25 @@
      * pinned positions can be positive integers that range anywhere from 0 to
      * {@link PinnedPositions#UNPINNED}.
      * </p>
-     *
-     * <p>
-     * When using {@link PinnedPositions#UPDATE_URI} to update the pinned positions of
-     * certain contacts, it may make sense for your application to star any pinned contacts
-     * by default. To specify this behavior, set the boolean query parameter
-     * {@link PinnedPositions#STAR_WHEN_PINNING} to true to force all pinned and unpinned
-     * contacts to be automatically starred and unstarred.
-     * </p>
-     *
-     * @hide
      */
     public static final class PinnedPositions {
-
         /**
          * <p>
-         * This URI allows applications to update pinned positions for a provided set of contacts.
-         * </p>
-         *
-         * <p>
-         * The list of contactIds to pin and their corresponding pinned positions should be
-         * provided in key-value pairs stored in a {@link ContentValues} object where the key
-         * is a valid contactId, while each pinned position is a positive integer.
+         * The method to invoke in order to undemote a formerly demoted contact. The contact id of
+         * the contact must be provided as an argument. If the contact was not previously demoted,
+         * nothing will be done.
          * </p>
          *
          * <p>
          * Example:
          * <pre>
-         * ContentValues values = new ContentValues();
-         * values.put("10", 20);
-         * values.put("12", 2);
-         * values.put("15", PinnedPositions.UNPINNED);
-         * int count = resolver.update(PinnedPositions.UPDATE_URI, values, null, null);
+         * final long contactId = 10;
+         * resolver.call(ContactsContract.AUTHORITY_URI, PinnedPositions.UNDEMOTE_METHOD,
+         *         String.valueOf(contactId), null);
          * </pre>
-         *
-         * This pins the contact with id 10 at position 20, the contact with id 12 at position 2,
-         * and unpins the contact with id 15.
          * </p>
          */
-        public static final Uri UPDATE_URI = Uri.withAppendedPath(AUTHORITY_URI,
-                "pinned_position_update");
+        public static final String UNDEMOTE_METHOD = "undemote";
 
         /**
          * Default value for the pinned position of an unpinned contact. Also equal to
@@ -7825,65 +7803,16 @@
          * just hidden from view.
          */
         public static final int DEMOTED = -1;
-
-        /**
-         * <p> Clients can provide this value as a pinned position to undemote a formerly demoted
-         * contact. If the contact was formerly demoted, it will be restored to an
-         * {@link #UNPINNED} position. If it was otherwise already pinned at another position,
-         * it will not be affected.
-         * </p>
-         *
-         * <p>
-         * Example:
-         * <pre>
-         * ContentValues values = new ContentValues();
-         * values.put("15", PinnedPositions.UNDEMOTE);
-         * int count = resolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon()
-         *          .build(), values, null, null);
-         * </pre>
-         *
-         * This restores the contact with id 15 to an {@link #UNPINNED} position, meaning that
-         * other apps (e.g. the Dialer) that were formerly hiding this contact from view based on
-         * its {@link #DEMOTED} position will start displaying it again.
-         * </p>
-         */
-        public static final String UNDEMOTE = "undemote";
-
-        /**
-         * <p>
-         * A boolean query parameter that can be used with {@link #UPDATE_URI}.
-         * If "1" or "true", any contact that is pinned or unpinned will be correspondingly
-         * starred or unstarred. Otherwise, starring information will not be affected by pinned
-         * updates. This is false by default.
-         * </p>
-         *
-         * <p>
-         * Example:
-         * <pre>
-         * ContentValues values = new ContentValues();
-         * values.put("10", 20);
-         * values.put("15", PinnedPositions.UNPINNED);
-         * int count = resolver.update(ContactsContract.PinnedPositions.UPDATE_URI.buildUpon()
-         *          .appendQueryParameter(PinnedPositions.FORCE_STAR_WHEN_PINNING, "true").build(),
-         *          values, null, null);
-         * </pre>
-         *
-         * This will pin the contact with id 10 at position 20 and star it automatically if not
-         * already starred, and unpin the contact with id 15, and unstar it automatically if not
-         * already unstarred.
-         * </p>
-         */
-        public static final String STAR_WHEN_PINNING = "star_when_pinning";
     }
 
     /**
-     * Helper methods to display QuickContact dialogs that allow users to pivot on
+     * Helper methods to display QuickContact dialogs that display all the information belonging to
      * a specific {@link Contacts} entry.
      */
     public static final class QuickContact {
         /**
-         * Action used to trigger person pivot dialog.
-         * @hide
+         * Action used to launch the system contacts application and bring up a QuickContact dialog
+         * for the provided {@link Contacts} entry.
          */
         public static final String ACTION_QUICK_CONTACT =
                 "com.android.contacts.action.QUICK_CONTACT";
@@ -7903,9 +7832,8 @@
         public static final String EXTRA_MODE = "mode";
 
         /**
-         * Extra used to indicate a list of specific MIME-types to exclude and
-         * not display. Stored as a {@link String} array.
-         * @hide
+         * Extra used to indicate a list of specific MIME-types to exclude and not display in the
+         * QuickContacts dialog. Stored as a {@link String} array.
          */
         public static final String EXTRA_EXCLUDE_MIMES = "exclude_mimes";
 
diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java
index 0fe764f..325917e 100644
--- a/core/java/android/provider/MediaStore.java
+++ b/core/java/android/provider/MediaStore.java
@@ -40,6 +40,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.Arrays;
 
 /**
  * The Media provider contains meta data for all available media on both internal
@@ -671,6 +672,7 @@
                         if (sThumbBuf == null) {
                             sThumbBuf = new byte[MiniThumbFile.BYTES_PER_MINTHUMB];
                         }
+                        Arrays.fill(sThumbBuf, (byte)0);
                         if (thumbFile.getMiniThumbFromFile(origId, sThumbBuf) != null) {
                             bitmap = BitmapFactory.decodeByteArray(sThumbBuf, 0, sThumbBuf.length);
                             if (bitmap == null) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 06c05ee..e76d70f 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2460,6 +2460,18 @@
         public static final String POINTER_SPEED = "pointer_speed";
 
         /**
+         * Whether lock-to-app will be triggered by long-press on recents.
+         * @hide
+         */
+        public static final String LOCK_TO_APP_ENABLED = "lock_to_app_enabled";
+
+        /**
+         * Whether lock-to-app will lock the keyguard when exiting.
+         * @hide
+         */
+        public static final String LOCK_TO_APP_EXIT_LOCKED = "lock_to_app_exit_locked";
+
+        /**
          * I am the lolrus.
          * <p>
          * Nonzero values indicate that the user has a bukkit.
@@ -4537,12 +4549,6 @@
         public static final String SMS_DEFAULT_APPLICATION = "sms_default_application";
 
         /**
-         * Specifies the package name currently configured to be the primary phone application
-         * @hide
-         */
-        public static final String PHONE_DEFAULT_APPLICATION = "phone_default_application";
-
-        /**
          * Name of a package that the current user has explicitly allowed to see all of that
          * user's notifications.
          *
@@ -4583,11 +4589,9 @@
 
         /**
          * If enabled, apps should try to skip any introductory hints on first launch. This might
-         * apply to users that are already familiar with the environment or temporary users, like
-         * guests.
+         * apply to users that are already familiar with the environment or temporary users.
          * <p>
          * Type : int (0 to show hints, 1 to skip showing hints)
-         * @hide
          */
         public static final String SKIP_FIRST_USE_HINTS = "skip_first_use_hints";
 
diff --git a/core/java/android/service/fingerprint/FingerprintManager.java b/core/java/android/service/fingerprint/FingerprintManager.java
index b6137d1..5fd597b 100644
--- a/core/java/android/service/fingerprint/FingerprintManager.java
+++ b/core/java/android/service/fingerprint/FingerprintManager.java
@@ -96,6 +96,9 @@
         }
     };
 
+    /**
+     * @hide
+     */
     public FingerprintManager(Context context, IFingerprintService service) {
         mContext = context;
         mService = service;
diff --git a/core/java/android/transition/Explode.java b/core/java/android/transition/Explode.java
index fae527c..feb8efd 100644
--- a/core/java/android/transition/Explode.java
+++ b/core/java/android/transition/Explode.java
@@ -15,20 +15,16 @@
  */
 package android.transition;
 
+import com.android.internal.R;
+
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
-import android.graphics.Path;
 import android.graphics.Rect;
 import android.util.FloatMath;
-import android.util.Log;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
-
 /**
  * This transition tracks changes to the visibility of target views in the
  * start and end scenes and moves views in or out from the edges of the
@@ -44,8 +40,7 @@
     private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
     private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
     private static final String TAG = "Explode";
-
-    private static final String PROPNAME_SCREEN_BOUNDS = "android:out:screenBounds";
+    private static final String PROPNAME_SCREEN_BOUNDS = "android:explode:screenBounds";
 
     private int[] mTempLoc = new int[2];
 
@@ -56,8 +51,8 @@
     private void captureValues(TransitionValues transitionValues) {
         View view = transitionValues.view;
         view.getLocationOnScreen(mTempLoc);
-        int left = mTempLoc[0] + Math.round(view.getTranslationX());
-        int top = mTempLoc[1] + Math.round(view.getTranslationY());
+        int left = mTempLoc[0];
+        int top = mTempLoc[1];
         int right = left + view.getWidth();
         int bottom = top + view.getHeight();
         transitionValues.values.put(PROPNAME_SCREEN_BOUNDS, new Rect(left, top, right, bottom));
@@ -75,27 +70,6 @@
         captureValues(transitionValues);
     }
 
-    private Animator createAnimation(final View view, float startX, float startY, float endX,
-            float endY, float terminalX, float terminalY, TimeInterpolator interpolator) {
-        view.setTranslationX(startX);
-        view.setTranslationY(startY);
-        if (startY == endY && startX == endX) {
-            return null;
-        }
-        Path path = new Path();
-        path.moveTo(startX, startY);
-        path.lineTo(endX, endY);
-        ObjectAnimator pathAnimator = ObjectAnimator.ofFloat(view, View.TRANSLATION_X,
-                View.TRANSLATION_Y, path);
-        pathAnimator.setInterpolator(interpolator);
-        OutAnimatorListener listener = new OutAnimatorListener(view, terminalX, terminalY,
-                endX, endY);
-        pathAnimator.addListener(listener);
-        pathAnimator.addPauseListener(listener);
-
-        return pathAnimator;
-    }
-
     @Override
     public Animator onAppear(ViewGroup sceneRoot, View view,
             TransitionValues startValues, TransitionValues endValues) {
@@ -103,29 +77,43 @@
             return null;
         }
         Rect bounds = (Rect) endValues.values.get(PROPNAME_SCREEN_BOUNDS);
+        float endX = view.getTranslationX();
+        float endY = view.getTranslationY();
         calculateOut(sceneRoot, bounds, mTempLoc);
+        float startX = endX + mTempLoc[0];
+        float startY = endY + mTempLoc[1];
 
-        final float endX = view.getTranslationX();
-        final float startX = endX + mTempLoc[0];
-        final float endY = view.getTranslationY();
-        final float startY = endY + mTempLoc[1];
-
-        return createAnimation(view, startX, startY, endX, endY, endX, endY, sDecelerate);
+        return TranslationAnimationCreator.createAnimation(view, endValues, bounds.left, bounds.top,
+                startX, startY, endX, endY, sDecelerate);
     }
 
     @Override
     public Animator onDisappear(ViewGroup sceneRoot, View view,
             TransitionValues startValues, TransitionValues endValues) {
+        if (startValues == null) {
+            return null;
+        }
         Rect bounds = (Rect) startValues.values.get(PROPNAME_SCREEN_BOUNDS);
+        int viewPosX = bounds.left;
+        int viewPosY = bounds.top;
+        float startX = view.getTranslationX();
+        float startY = view.getTranslationY();
+        float endX = startX;
+        float endY = startY;
+        int[] interruptedPosition = (int[]) startValues.view.getTag(R.id.transitionPosition);
+        if (interruptedPosition != null) {
+            // We want to have the end position relative to the interrupted position, not
+            // the position it was supposed to start at.
+            endX += interruptedPosition[0] - bounds.left;
+            endY += interruptedPosition[1] - bounds.top;
+            bounds.offsetTo(interruptedPosition[0], interruptedPosition[1]);
+        }
         calculateOut(sceneRoot, bounds, mTempLoc);
+        endX += mTempLoc[0];
+        endY += mTempLoc[1];
 
-        final float startX = view.getTranslationX();
-        final float endX = startX + mTempLoc[0];
-        final float startY = view.getTranslationY();
-        final float endY = startY + mTempLoc[1];
-
-        return createAnimation(view, startX, startY, endX, endY, startX, startY,
-                sAccelerate);
+        return TranslationAnimationCreator.createAnimation(view, startValues,
+                viewPosX, viewPosY, startX, startY, endX, endY, sAccelerate);
     }
 
     private void calculateOut(View sceneRoot, Rect bounds, int[] outVector) {
@@ -153,8 +141,8 @@
 
         if (xVector == 0 && yVector == 0) {
             // Random direction when View is centered on focal View.
-            xVector = (float)(Math.random() * 2) - 1;
-            yVector = (float)(Math.random() * 2) - 1;
+            xVector = (float) (Math.random() * 2) - 1;
+            yVector = (float) (Math.random() * 2) - 1;
         }
         float vectorSize = calculateDistance(xVector, yVector);
         xVector /= vectorSize;
@@ -176,53 +164,4 @@
     private static float calculateDistance(float x, float y) {
         return FloatMath.sqrt((x * x) + (y * y));
     }
-
-    private static class OutAnimatorListener extends AnimatorListenerAdapter {
-        private final View mView;
-        private boolean mCanceled = false;
-        private float mPausedX;
-        private float mPausedY;
-        private final float mTerminalX;
-        private final float mTerminalY;
-        private final float mEndX;
-        private final float mEndY;
-
-        public OutAnimatorListener(View view, float terminalX, float terminalY,
-                float endX, float endY) {
-            mView = view;
-            mTerminalX = terminalX;
-            mTerminalY = terminalY;
-            mEndX = endX;
-            mEndY = endY;
-        }
-
-        @Override
-        public void onAnimationCancel(Animator animator) {
-            mView.setTranslationX(mTerminalX);
-            mView.setTranslationY(mTerminalY);
-            mCanceled = true;
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animator) {
-            if (!mCanceled) {
-                mView.setTranslationX(mTerminalX);
-                mView.setTranslationY(mTerminalY);
-            }
-        }
-
-        @Override
-        public void onAnimationPause(Animator animator) {
-            mPausedX = mView.getTranslationX();
-            mPausedY = mView.getTranslationY();
-            mView.setTranslationY(mEndX);
-            mView.setTranslationY(mEndY);
-        }
-
-        @Override
-        public void onAnimationResume(Animator animator) {
-            mView.setTranslationX(mPausedX);
-            mView.setTranslationY(mPausedY);
-        }
-    }
 }
diff --git a/core/java/android/transition/Slide.java b/core/java/android/transition/Slide.java
index 8269258..0d2e487 100644
--- a/core/java/android/transition/Slide.java
+++ b/core/java/android/transition/Slide.java
@@ -16,14 +16,7 @@
 package android.transition;
 
 import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.animation.AnimatorSet;
-import android.animation.ObjectAnimator;
 import android.animation.TimeInterpolator;
-import android.animation.ValueAnimator;
-import android.graphics.Rect;
-import android.util.Log;
-import android.util.Property;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
@@ -41,71 +34,60 @@
  */
 public class Slide extends Visibility {
     private static final String TAG = "Slide";
-
     private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
     private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
-
+    private static final String PROPNAME_SCREEN_POSITION = "android:slide:screenPosition";
     private CalculateSlide mSlideCalculator = sCalculateBottom;
 
     private interface CalculateSlide {
-        /** Returns the translation value for view when it out of the scene */
-        float getGone(ViewGroup sceneRoot, View view);
 
-        /** Returns the translation value for view when it is in the scene */
-        float getHere(View view);
+        /** Returns the translation value for view when it goes out of the scene */
+        float getGoneX(ViewGroup sceneRoot, View view);
 
-        /** Returns the property to animate translation */
-        Property<View, Float> getProperty();
+        /** Returns the translation value for view when it goes out of the scene */
+        float getGoneY(ViewGroup sceneRoot, View view);
     }
 
     private static abstract class CalculateSlideHorizontal implements CalculateSlide {
-        @Override
-        public float getHere(View view) {
-            return view.getTranslationX();
-        }
 
         @Override
-        public Property<View, Float> getProperty() {
-            return View.TRANSLATION_X;
+        public float getGoneY(ViewGroup sceneRoot, View view) {
+            return view.getTranslationY();
         }
     }
 
     private static abstract class CalculateSlideVertical implements CalculateSlide {
-        @Override
-        public float getHere(View view) {
-            return view.getTranslationY();
-        }
 
         @Override
-        public Property<View, Float> getProperty() {
-            return View.TRANSLATION_Y;
+        public float getGoneX(ViewGroup sceneRoot, View view) {
+            return view.getTranslationX();
         }
     }
 
     private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
         @Override
-        public float getGone(ViewGroup sceneRoot, View view) {
+        public float getGoneX(ViewGroup sceneRoot, View view) {
             return view.getTranslationX() - sceneRoot.getWidth();
         }
     };
 
     private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
         @Override
-        public float getGone(ViewGroup sceneRoot, View view) {
+        public float getGoneY(ViewGroup sceneRoot, View view) {
             return view.getTranslationY() - sceneRoot.getHeight();
         }
     };
 
     private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
         @Override
-        public float getGone(ViewGroup sceneRoot, View view) {
+        public float getGoneX(ViewGroup sceneRoot, View view) {
             return view.getTranslationX() + sceneRoot.getWidth();
         }
     };
 
     private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
         @Override
-        public float getGone(ViewGroup sceneRoot, View view) {
+        public float getGoneY(ViewGroup sceneRoot, View view) {
             return view.getTranslationY() + sceneRoot.getHeight();
         }
     };
@@ -125,8 +107,28 @@
         setSlideEdge(slideEdge);
     }
 
+    private void captureValues(TransitionValues transitionValues) {
+        View view = transitionValues.view;
+        int[] position = new int[2];
+        view.getLocationOnScreen(position);
+        transitionValues.values.put(PROPNAME_SCREEN_POSITION, position);
+    }
+
+    @Override
+    public void captureStartValues(TransitionValues transitionValues) {
+        super.captureStartValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
+    @Override
+    public void captureEndValues(TransitionValues transitionValues) {
+        super.captureEndValues(transitionValues);
+        captureValues(transitionValues);
+    }
+
     /**
      * Change the edge that Views appear and disappear from.
+     *
      * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
      *                  {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
      *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM}.
@@ -153,77 +155,35 @@
         setPropagation(propagation);
     }
 
-    private Animator createAnimation(final View view, Property<View, Float> property,
-            float start, float end, float terminalValue, TimeInterpolator interpolator) {
-        view.setTranslationY(start);
-        if (start == end) {
-            return null;
-        }
-        final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end);
-
-        SlideAnimatorListener listener = new SlideAnimatorListener(view, terminalValue, end);
-        anim.addListener(listener);
-        anim.addPauseListener(listener);
-        anim.setInterpolator(interpolator);
-        return anim;
-    }
-
     @Override
     public Animator onAppear(ViewGroup sceneRoot, View view,
             TransitionValues startValues, TransitionValues endValues) {
         if (endValues == null) {
             return null;
         }
-        float end = mSlideCalculator.getHere(view);
-        float start = mSlideCalculator.getGone(sceneRoot, view);
-        return createAnimation(view, mSlideCalculator.getProperty(), start, end, end, sDecelerate);
+        int[] position = (int[]) endValues.values.get(PROPNAME_SCREEN_POSITION);
+        float endX = view.getTranslationX();
+        float endY = view.getTranslationY();
+        float startX = mSlideCalculator.getGoneX(sceneRoot, view);
+        float startY = mSlideCalculator.getGoneY(sceneRoot, view);
+        return TranslationAnimationCreator
+                .createAnimation(view, endValues, position[0], position[1],
+                        startX, startY, endX, endY, sDecelerate);
     }
 
     @Override
     public Animator onDisappear(ViewGroup sceneRoot, View view,
             TransitionValues startValues, TransitionValues endValues) {
-        float start = mSlideCalculator.getHere(view);
-        float end = mSlideCalculator.getGone(sceneRoot, view);
-
-        return createAnimation(view, mSlideCalculator.getProperty(), start, end, start,
-                sAccelerate);
-    }
-
-    private static class SlideAnimatorListener extends AnimatorListenerAdapter {
-        private boolean mCanceled = false;
-        private float mPausedY;
-        private final View mView;
-        private final float mEndY;
-        private final float mTerminalY;
-
-        public SlideAnimatorListener(View view, float terminalY, float endY) {
-            mView = view;
-            mTerminalY = terminalY;
-            mEndY = endY;
+        if (startValues == null) {
+            return null;
         }
-
-        @Override
-        public void onAnimationCancel(Animator animator) {
-            mView.setTranslationY(mTerminalY);
-            mCanceled = true;
-        }
-
-        @Override
-        public void onAnimationEnd(Animator animator) {
-            if (!mCanceled) {
-                mView.setTranslationY(mTerminalY);
-            }
-        }
-
-        @Override
-        public void onAnimationPause(Animator animator) {
-            mPausedY = mView.getTranslationY();
-            mView.setTranslationY(mEndY);
-        }
-
-        @Override
-        public void onAnimationResume(Animator animator) {
-            mView.setTranslationY(mPausedY);
-        }
+        int[] position = (int[]) startValues.values.get(PROPNAME_SCREEN_POSITION);
+        float startX = view.getTranslationX();
+        float startY = view.getTranslationY();
+        float endX = mSlideCalculator.getGoneX(sceneRoot, view);
+        float endY = mSlideCalculator.getGoneY(sceneRoot, view);
+        return TranslationAnimationCreator
+                .createAnimation(view, startValues, position[0], position[1],
+                        startX, startY, endX, endY, sAccelerate);
     }
 }
diff --git a/core/java/android/transition/Transition.java b/core/java/android/transition/Transition.java
index 508769d..0017eb1 100644
--- a/core/java/android/transition/Transition.java
+++ b/core/java/android/transition/Transition.java
@@ -89,8 +89,8 @@
  * out-in behavior. Finally, note the use of the <code>targets</code> sub-tag, which
  * takes a set of {@link android.R.styleable#TransitionTarget target} tags, each
  * of which lists a specific <code>targetId</code>, <code>targetClass</code>,
- * <code>targetViewName</code>, <code>excludeId</code>, <code>excludeClass</code>, or
- * <code>excludeViewName</code>, which this transition acts upon.
+ * <code>targetName</code>, <code>excludeId</code>, <code>excludeClass</code>, or
+ * <code>excludeName</code>, which this transition acts upon.
  * Use of targets is optional, but can be used to either limit the time spent checking
  * attributes on unchanging views, or limiting the types of animations run on specific views.
  * In this case, we know that only the <code>grayscaleContainer</code> will be
@@ -115,9 +115,9 @@
 
     /**
      * With {@link #setMatchOrder(int...)}, chooses to match by
-     * {@link android.view.View#getViewName()}. Null names will not be matched.
+     * {@link android.view.View#getTransitionName()}. Null names will not be matched.
      */
-    public static final int MATCH_VIEW_NAME = 0x2;
+    public static final int MATCH_NAME = 0x2;
 
     /**
      * With {@link #setMatchOrder(int...)}, chooses to match by
@@ -135,7 +135,7 @@
     private static final int MATCH_LAST = MATCH_ITEM_ID;
 
     private static final int[] DEFAULT_MATCH_ORDER = {
-        MATCH_VIEW_NAME,
+        MATCH_NAME,
         MATCH_INSTANCE,
         MATCH_ID,
         MATCH_ITEM_ID,
@@ -207,7 +207,7 @@
     EpicenterCallback mEpicenterCallback;
 
     // For Fragment shared element transitions, linking views explicitly by mismatching
-    // viewNames.
+    // transitionNames.
     ArrayMap<String, String> mNameOverrides;
 
     /**
@@ -378,17 +378,17 @@
     /**
      * Sets the order in which Transition matches View start and end values.
      * <p>
-     * The default behavior is to match first by {@link android.view.View#getViewName()},
+     * The default behavior is to match first by {@link android.view.View#getTransitionName()},
      * then by View instance, then by {@link android.view.View#getId()} and finally
      * by its item ID if it is in a direct child of ListView. The caller can
      * choose to have only some or all of the values of {@link #MATCH_INSTANCE},
-     * {@link #MATCH_VIEW_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}. Only
+     * {@link #MATCH_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}. Only
      * the match algorithms supplied will be used to determine whether Views are the
      * the same in both the start and end Scene. Views that do not match will be considered
      * as entering or leaving the Scene.
      * </p>
      * @param matches A list of zero or more of {@link #MATCH_INSTANCE},
-     *                {@link #MATCH_VIEW_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}.
+     *                {@link #MATCH_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}.
      *                If none are provided, then the default match order will be set.
      */
     public void setMatchOrder(int... matches) {
@@ -500,9 +500,9 @@
     }
 
     /**
-     * Match start/end values by Adapter viewName. Adds matched values to startValuesList
+     * Match start/end values by Adapter transitionName. Adds matched values to startValuesList
      * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using
-     * startNames and endNames as a guide for which Views have unique viewNames.
+     * startNames and endNames as a guide for which Views have unique transitionNames.
      */
     private void matchNames(ArrayList<TransitionValues> startValuesList,
             ArrayList<TransitionValues> endValuesList,
@@ -563,7 +563,7 @@
                 case MATCH_INSTANCE:
                     matchInstances(startValuesList, endValuesList, unmatchedStart, unmatchedEnd);
                     break;
-                case MATCH_VIEW_NAME:
+                case MATCH_NAME:
                     matchNames(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
                             startValues.nameValues, endValues.nameValues);
                     break;
@@ -673,7 +673,7 @@
                             startDelays.put(mAnimators.size(), delay);
                             minStartDelay = Math.min(delay, minStartDelay);
                         }
-                        AnimationInfo info = new AnimationInfo(view, getName(),
+                        AnimationInfo info = new AnimationInfo(view, getName(), this,
                                 sceneRoot.getWindowId(), infoValues);
                         runningAnimators.put(animator, info);
                         mAnimators.add(animator);
@@ -718,8 +718,8 @@
                 }
             }
         }
-        if (mTargetNameExcludes != null && target != null && target.getViewName() != null) {
-            if (mTargetNameExcludes.contains(target.getViewName())) {
+        if (mTargetNameExcludes != null && target != null && target.getTransitionName() != null) {
+            if (mTargetNameExcludes.contains(target.getTransitionName())) {
                 return false;
             }
         }
@@ -731,7 +731,7 @@
         if (mTargetIds.contains(targetId) || mTargets.contains(target)) {
             return true;
         }
-        if (mTargetNames != null && mTargetNames.contains(target.getViewName())) {
+        if (mTargetNames != null && mTargetNames.contains(target.getTransitionName())) {
             return true;
         }
         if (mTargetTypes != null) {
@@ -882,18 +882,18 @@
     }
 
     /**
-     * Adds the viewName of a target view that this Transition is interested in
+     * Adds the transitionName of a target view that this Transition is interested in
      * animating. By default, there are no targetNames, and a Transition will
      * listen for changes on every view in the hierarchy below the sceneRoot
      * of the Scene being transitioned into. Setting targetNames constrains
-     * the Transition to only listen for, and act on, views with these viewNames.
-     * Views with different viewNames, or no viewName whatsoever, will be ignored.
+     * the Transition to only listen for, and act on, views with these transitionNames.
+     * Views with different transitionNames, or no transitionName whatsoever, will be ignored.
      *
-     * <p>Note that viewNames should be unique within the view hierarchy.</p>
+     * <p>Note that transitionNames should be unique within the view hierarchy.</p>
      *
-     * @see android.view.View#getViewName()
-     * @param targetName The viewName of a target view, must be non-null.
-     * @return The Transition to which the target viewName is added.
+     * @see android.view.View#getTransitionName()
+     * @param targetName The transitionName of a target view, must be non-null.
+     * @return The Transition to which the target transitionName is added.
      * Returning the same object makes it easier to chain calls during
      * construction, such as
      * <code>transitionSet.addTransitions(new Fade()).addTarget(someName);</code>
@@ -958,10 +958,10 @@
     }
 
     /**
-     * Removes the given targetName from the list of viewNames that this Transition
+     * Removes the given targetName from the list of transitionNames that this Transition
      * is interested in animating.
      *
-     * @param targetName The viewName of a target view, must not be null.
+     * @param targetName The transitionName of a target view, must not be null.
      * @return The Transition from which the targetName is removed.
      * Returning the same object makes it easier to chain calls during
      * construction, such as
@@ -1003,28 +1003,28 @@
     }
 
     /**
-     * Whether to add the given viewName to the list of target viewNames to exclude from this
-     * transition. The <code>exclude</code> parameter specifies whether the target
+     * Whether to add the given transitionName to the list of target transitionNames to exclude
+     * from this transition. The <code>exclude</code> parameter specifies whether the target
      * should be added to or removed from the excluded list.
      *
      * <p>Excluding targets is a general mechanism for allowing transitions to run on
      * a view hierarchy while skipping target views that should not be part of
      * the transition. For example, you may want to avoid animating children
      * of a specific ListView or Spinner. Views can be excluded by their
-     * id, their instance reference, their viewName, or by the Class of that view
+     * id, their instance reference, their transitionName, or by the Class of that view
      * (eg, {@link Spinner}).</p>
      *
      * @see #excludeTarget(View, boolean)
      * @see #excludeTarget(int, boolean)
      * @see #excludeTarget(Class, boolean)
      *
-     * @param targetViewName The name of a target to ignore when running this transition.
+     * @param targetName The name of a target to ignore when running this transition.
      * @param exclude Whether to add the target to or remove the target from the
      * current list of excluded targets.
      * @return This transition object.
      */
-    public Transition excludeTarget(String targetViewName, boolean exclude) {
-        mTargetNameExcludes = excludeObject(mTargetNameExcludes, targetViewName, exclude);
+    public Transition excludeTarget(String targetName, boolean exclude) {
+        mTargetNameExcludes = excludeObject(mTargetNameExcludes, targetName, exclude);
         return this;
     }
 
@@ -1248,7 +1248,7 @@
     /**
      * Returns the list of target IDs that this transition limits itself to
      * tracking and animating. If the list is null or empty for
-     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
+     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetNames()}, and
      * {@link #getTargetTypes()} then this transition is
      * not limited to specific views, and will handle changes to any views
      * in the hierarchy of a scene change.
@@ -1262,7 +1262,7 @@
     /**
      * Returns the list of target views that this transition limits itself to
      * tracking and animating. If the list is null or empty for
-     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
+     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetNames()}, and
      * {@link #getTargetTypes()} then this transition is
      * not limited to specific views, and will handle changes to any views
      * in the hierarchy of a scene change.
@@ -1274,23 +1274,31 @@
     }
 
     /**
-     * Returns the list of target viewNames that this transition limits itself to
+     * Returns the list of target transitionNames that this transition limits itself to
      * tracking and animating. If the list is null or empty for
-     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
+     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetNames()}, and
      * {@link #getTargetTypes()} then this transition is
      * not limited to specific views, and will handle changes to any views
      * in the hierarchy of a scene change.
      *
-     * @return the list of target viewNames
+     * @return the list of target transitionNames
+     */
+    public List<String> getTargetNames() {
+        return mTargetNames;
+    }
+
+    /**
+     * To be removed before L release.
+     * @hide
      */
     public List<String> getTargetViewNames() {
         return mTargetNames;
     }
 
     /**
-     * Returns the list of target viewNames that this transition limits itself to
+     * Returns the list of target transitionNames that this transition limits itself to
      * tracking and animating. If the list is null or empty for
-     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
+     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetNames()}, and
      * {@link #getTargetTypes()} then this transition is
      * not limited to specific views, and will handle changes to any views
      * in the hierarchy of a scene change.
@@ -1380,10 +1388,10 @@
                 transitionValuesMaps.idValues.put(id, view);
             }
         }
-        String name = view.getViewName();
+        String name = view.getTransitionName();
         if (name != null) {
             if (transitionValuesMaps.nameValues.containsKey(name)) {
-                // Duplicate viewNames: cannot match by viewName.
+                // Duplicate transitionNames: cannot match by transitionName.
                 transitionValuesMaps.nameValues.put(name, null);
             } else {
                 transitionValuesMaps.nameValues.put(name, view);
@@ -1587,30 +1595,10 @@
                 AnimationInfo oldInfo = runningAnimators.get(anim);
                 if (oldInfo != null && oldInfo.view != null &&
                         oldInfo.view.getContext() == sceneRoot.getContext()) {
-                    boolean cancel = false;
                     TransitionValues oldValues = oldInfo.values;
                     View oldView = oldInfo.view;
                     TransitionValues newValues = mEndValues.viewValues.get(oldView);
-                    if (oldValues != null) {
-                        // if oldValues null, then transition didn't care to stash values,
-                        // and won't get canceled
-                        if (newValues != null) {
-                            for (String key : oldValues.values.keySet()) {
-                                Object oldValue = oldValues.values.get(key);
-                                Object newValue = newValues.values.get(key);
-                                if (oldValue != null && newValue != null &&
-                                        !oldValue.equals(newValue)) {
-                                    cancel = true;
-                                    if (DBG) {
-                                        Log.d(LOG_TAG, "Transition.playTransition: " +
-                                                "oldValue != newValue for " + key +
-                                                ": old, new = " + oldValue + ", " + newValue);
-                                    }
-                                    break;
-                                }
-                            }
-                        }
-                    }
+                    boolean cancel = oldInfo.transition.areValuesChanged(oldValues, newValues);
                     if (cancel) {
                         if (anim.isRunning() || anim.isStarted()) {
                             if (DBG) {
@@ -1632,6 +1620,29 @@
         runAnimators();
     }
 
+    boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) {
+        boolean valuesChanged = false;
+        // if oldValues null, then transition didn't care to stash values,
+        // and won't get canceled
+        if (oldValues != null && newValues != null) {
+            for (String key : oldValues.values.keySet()) {
+                Object oldValue = oldValues.values.get(key);
+                Object newValue = newValues.values.get(key);
+                if (oldValue != null && newValue != null &&
+                        !oldValue.equals(newValue)) {
+                    valuesChanged = true;
+                    if (DBG) {
+                        Log.d(LOG_TAG, "Transition.playTransition: " +
+                                "oldValue != newValue for " + key +
+                                ": old, new = " + oldValue + ", " + newValue);
+                    }
+                    break;
+                }
+            }
+        }
+        return valuesChanged;
+    }
+
     /**
      * This is a utility method used by subclasses to handle standard parts of
      * setting up and running an Animator: it sets the {@link #getDuration()
@@ -2070,12 +2081,15 @@
         String name;
         TransitionValues values;
         WindowId windowId;
+        Transition transition;
 
-        AnimationInfo(View view, String name, WindowId windowId, TransitionValues values) {
+        AnimationInfo(View view, String name, Transition transition,
+                WindowId windowId, TransitionValues values) {
             this.view = view;
             this.name = name;
             this.values = values;
             this.windowId = windowId;
+            this.transition = transition;
         }
     }
 
diff --git a/core/java/android/transition/TransitionInflater.java b/core/java/android/transition/TransitionInflater.java
index 5b7c737..551f78c 100644
--- a/core/java/android/transition/TransitionInflater.java
+++ b/core/java/android/transition/TransitionInflater.java
@@ -30,7 +30,6 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
-import java.util.ArrayList;
 import java.util.StringTokenizer;
 
 /**
@@ -43,6 +42,8 @@
  */
 public class TransitionInflater {
     private static final String MATCH_INSTANCE = "instance";
+    private static final String MATCH_NAME = "name";
+    /** To be removed before L release */
     private static final String MATCH_VIEW_NAME = "viewName";
     private static final String MATCH_ID = "id";
     private static final String MATCH_ITEM_ID = "itemId";
@@ -90,8 +91,6 @@
     /**
      * Loads a {@link TransitionManager} object from a resource
      *
-     *
-     *
      * @param resource The resource id of the transition manager to load
      * @return The loaded TransitionManager object
      * @throws android.content.res.Resources.NotFoundException when the
@@ -235,20 +234,20 @@
                         com.android.internal.R.styleable.TransitionTarget);
                 int id = a.getResourceId(
                         com.android.internal.R.styleable.TransitionTarget_targetId, -1);
-                String viewName;
+                String transitionName;
                 if (id >= 0) {
                     transition.addTarget(id);
                 } else if ((id = a.getResourceId(
                         com.android.internal.R.styleable.TransitionTarget_excludeId, -1)) >= 0) {
                     transition.excludeTarget(id, true);
-                } else if ((viewName = a.getString(
-                            com.android.internal.R.styleable.TransitionTarget_targetViewName))
+                } else if ((transitionName = a.getString(
+                            com.android.internal.R.styleable.TransitionTarget_targetName))
                         != null) {
-                    transition.addTarget(viewName);
-                } else if ((viewName = a.getString(
-                        com.android.internal.R.styleable.TransitionTarget_excludeViewName))
+                    transition.addTarget(transitionName);
+                } else if ((transitionName = a.getString(
+                        com.android.internal.R.styleable.TransitionTarget_excludeName))
                         != null) {
-                    transition.excludeTarget(viewName, true);
+                    transition.excludeTarget(transitionName, true);
                 } else {
                     String className = a.getString(
                             com.android.internal.R.styleable.TransitionTarget_excludeClass);
@@ -282,8 +281,10 @@
                 matches[index] = Transition.MATCH_ID;
             } else if (MATCH_INSTANCE.equalsIgnoreCase(token)) {
                 matches[index] = Transition.MATCH_INSTANCE;
+            } else if (MATCH_NAME.equalsIgnoreCase(token)) {
+                matches[index] = Transition.MATCH_NAME;
             } else if (MATCH_VIEW_NAME.equalsIgnoreCase(token)) {
-                matches[index] = Transition.MATCH_VIEW_NAME;
+                matches[index] = Transition.MATCH_NAME;
             } else if (MATCH_ITEM_ID.equalsIgnoreCase(token)) {
                 matches[index] = Transition.MATCH_ITEM_ID;
             } else if (token.isEmpty()) {
diff --git a/core/java/android/transition/TransitionSet.java b/core/java/android/transition/TransitionSet.java
index c04be4f..495814a 100644
--- a/core/java/android/transition/TransitionSet.java
+++ b/core/java/android/transition/TransitionSet.java
@@ -244,11 +244,11 @@
     }
 
     @Override
-    public Transition excludeTarget(String targetViewName, boolean exclude) {
+    public Transition excludeTarget(String targetName, boolean exclude) {
         for (int i = 0; i < mTransitions.size(); i++) {
-            mTransitions.get(i).excludeTarget(targetViewName, exclude);
+            mTransitions.get(i).excludeTarget(targetName, exclude);
         }
-        return super.excludeTarget(targetViewName, exclude);
+        return super.excludeTarget(targetName, exclude);
     }
 
     @Override
diff --git a/core/java/android/transition/TranslationAnimationCreator.java b/core/java/android/transition/TranslationAnimationCreator.java
new file mode 100644
index 0000000..de71fd7
--- /dev/null
+++ b/core/java/android/transition/TranslationAnimationCreator.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014 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.transition;
+
+import com.android.internal.R;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.graphics.Path;
+import android.view.View;
+
+/**
+ * This class is used by Slide and Explode to create an animator that goes from the start
+ * position to the end position. It takes into account the canceled position so that it
+ * will not blink out or shift suddenly when the transition is interrupted.
+ */
+class TranslationAnimationCreator {
+
+    /**
+     * Creates an animator that can be used for x and/or y translations. When interrupted,
+     * it sets a tag to keep track of the position so that it may be continued from position.
+     *
+     * @param view The view being moved. This may be in the overlay for onDisappear.
+     * @param values The values containing the view in the view hierarchy.
+     * @param viewPosX The x screen coordinate of view
+     * @param viewPosY The y screen coordinate of view
+     * @param startX The start translation x of view
+     * @param startY The start translation y of view
+     * @param endX The end translation x of view
+     * @param endY The end translation y of view
+     * @param interpolator The interpolator to use with this animator.
+     * @return An animator that moves from (startX, startY) to (endX, endY) unless there was
+     * a previous interruption, in which case it moves from the current position to (endX, endY).
+     */
+    static Animator createAnimation(View view, TransitionValues values, int viewPosX, int viewPosY,
+            float startX, float startY, float endX, float endY, TimeInterpolator interpolator) {
+        float terminalX = view.getTranslationX();
+        float terminalY = view.getTranslationY();
+        int[] startPosition = (int[]) values.view.getTag(R.id.transitionPosition);
+        if (startPosition != null) {
+            startX = startPosition[0] - viewPosX + terminalX;
+            startY = startPosition[1] - viewPosY + terminalY;
+        }
+        // Initial position is at translation startX, startY, so position is offset by that amount
+        int startPosX = viewPosX + Math.round(startX - terminalX);
+        int startPosY = viewPosY + Math.round(startY - terminalY);
+
+        view.setTranslationX(startX);
+        view.setTranslationY(startY);
+        if (startX == endX && startY == endY) {
+            return null;
+        }
+        Path path = new Path();
+        path.moveTo(startX, startY);
+        path.lineTo(endX, endY);
+        ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y,
+                path);
+
+        TransitionPositionListener listener = new TransitionPositionListener(view, values.view,
+                startPosX, startPosY, terminalX, terminalY);
+        anim.addListener(listener);
+        anim.addPauseListener(listener);
+        anim.setInterpolator(interpolator);
+        return anim;
+    }
+
+    private static class TransitionPositionListener extends AnimatorListenerAdapter {
+
+        private final View mViewInHierarchy;
+        private final View mMovingView;
+        private final int mStartX;
+        private final int mStartY;
+        private int[] mTransitionPosition;
+        private float mPausedX;
+        private float mPausedY;
+        private final float mTerminalX;
+        private final float mTerminalY;
+
+        private TransitionPositionListener(View movingView, View viewInHierarchy,
+                int startX, int startY, float terminalX, float terminalY) {
+            mMovingView = movingView;
+            mViewInHierarchy = viewInHierarchy;
+            mStartX = startX - Math.round(mMovingView.getTranslationX());
+            mStartY = startY - Math.round(mMovingView.getTranslationY());
+            mTerminalX = terminalX;
+            mTerminalY = terminalY;
+            mTransitionPosition = (int[]) mViewInHierarchy.getTag(R.id.transitionPosition);
+            if (mTransitionPosition != null) {
+                mViewInHierarchy.setTagInternal(R.id.transitionPosition, null);
+            }
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            if (mTransitionPosition == null) {
+                mTransitionPosition = new int[2];
+            }
+            mTransitionPosition[0] = Math.round(mStartX + mMovingView.getTranslationX());
+            mTransitionPosition[1] = Math.round(mStartY + mMovingView.getTranslationY());
+            mViewInHierarchy.setTagInternal(R.id.transitionPosition, mTransitionPosition);
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animator) {
+            mMovingView.setTranslationX(mTerminalX);
+            mMovingView.setTranslationY(mTerminalY);
+        }
+
+        @Override
+        public void onAnimationPause(Animator animator) {
+            mPausedX = mMovingView.getTranslationX();
+            mPausedY = mMovingView.getTranslationY();
+            mMovingView.setTranslationX(mTerminalX);
+            mMovingView.setTranslationY(mTerminalY);
+        }
+
+        @Override
+        public void onAnimationResume(Animator animator) {
+            mMovingView.setTranslationX(mPausedX);
+            mMovingView.setTranslationY(mPausedY);
+        }
+    }
+
+}
diff --git a/core/java/android/transition/Visibility.java b/core/java/android/transition/Visibility.java
index c6c8337..947e1a7 100644
--- a/core/java/android/transition/Visibility.java
+++ b/core/java/android/transition/Visibility.java
@@ -331,16 +331,6 @@
                     public void onAnimationEnd(Animator animation) {
                         finalSceneRoot.getOverlay().remove(finalOverlayView);
                     }
-
-                    @Override
-                    public void onAnimationPause(Animator animation) {
-                        finalSceneRoot.getOverlay().remove(finalOverlayView);
-                    }
-
-                    @Override
-                    public void onAnimationResume(Animator animation) {
-                        finalSceneRoot.getOverlay().add(finalOverlayView);
-                    }
                 });
             }
             return animator;
@@ -409,6 +399,16 @@
         return overlayView;
     }
 
+    @Override
+    boolean areValuesChanged(TransitionValues oldValues, TransitionValues newValues) {
+        VisibilityInfo changeInfo = getVisibilityChangeInfo(oldValues, newValues);
+        if (oldValues == null && newValues == null) {
+            return false;
+        }
+        return changeInfo.visibilityChange && (changeInfo.startVisibility == View.VISIBLE ||
+            changeInfo.endVisibility == View.VISIBLE);
+    }
+
     /**
      * The default implementation of this method returns a null Animator. Subclasses should
      * override this method to make targets disappear with the desired transition. The
diff --git a/core/java/android/util/PathParser.java b/core/java/android/util/PathParser.java
new file mode 100644
index 0000000..f4a0448
--- /dev/null
+++ b/core/java/android/util/PathParser.java
@@ -0,0 +1,609 @@
+/*
+ * Copyright (C) 2014 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;
+
+import android.graphics.Path;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+/**
+ * @hide
+ */
+public class PathParser {
+    static final String LOGTAG = PathParser.class.getSimpleName();
+
+    /**
+     * @param pathData The string representing a path, the same as "d" string in svg file.
+     * @return the generated Path object.
+     */
+    public static Path createPathFromPathData(String pathData) {
+        Path path = new Path();
+        PathDataNode[] nodes = createNodesFromPathData(pathData);
+        if (nodes != null) {
+            PathDataNode.nodesToPath(nodes, path);
+            return path;
+        }
+        return null;
+    }
+
+    /**
+     * @param pathData The string representing a path, the same as "d" string in svg file.
+     * @return an array of the PathDataNode.
+     */
+    public static PathDataNode[] createNodesFromPathData(String pathData) {
+        if (pathData == null) {
+            return null;
+        }
+        int start = 0;
+        int end = 1;
+
+        ArrayList<PathDataNode> list = new ArrayList<PathDataNode>();
+        while (end < pathData.length()) {
+            end = nextStart(pathData, end);
+            String s = pathData.substring(start, end);
+            float[] val = getFloats(s);
+            addNode(list, s.charAt(0), val);
+
+            start = end;
+            end++;
+        }
+        if ((end - start) == 1 && start < pathData.length()) {
+            addNode(list, pathData.charAt(start), new float[0]);
+        }
+        return list.toArray(new PathDataNode[list.size()]);
+    }
+
+    /**
+     * @param source The array of PathDataNode to be duplicated.
+     * @return a deep copy of the <code>source</code>.
+     */
+    public static PathDataNode[] deepCopyNodes(PathDataNode[] source) {
+        PathDataNode[] copy = new PathParser.PathDataNode[source.length];
+        for (int i = 0; i < source.length; i ++) {
+            copy[i] = new PathDataNode(source[i]);
+        }
+        return copy;
+    }
+
+    /**
+     * @param nodesFrom The source path represented in an array of PathDataNode
+     * @param nodesTo The target path represented in an array of PathDataNode
+     * @return whether the <code>nodesFrom</code> can morph into <code>nodesTo</code>
+     */
+    public static boolean canMorph(PathDataNode[] nodesFrom, PathDataNode[] nodesTo) {
+        if (nodesFrom == null || nodesTo == null) {
+            return false;
+        }
+
+        if (nodesFrom.length != nodesTo.length) {
+            return false;
+        }
+
+        for (int i = 0; i < nodesFrom.length; i ++) {
+            if (nodesFrom[i].mType != nodesTo[i].mType
+                    || nodesFrom[i].mParams.length != nodesTo[i].mParams.length) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Update the target's data to match the source.
+     * Before calling this, make sure canMorph(target, source) is true.
+     *
+     * @param target The target path represented in an array of PathDataNode
+     * @param source The source path represented in an array of PathDataNode
+     */
+    public static void updateNodes(PathDataNode[] target, PathDataNode[] source) {
+        for (int i = 0; i < source.length; i ++) {
+            target[i].mType = source[i].mType;
+            for (int j = 0; j < source[i].mParams.length; j ++) {
+                target[i].mParams[j] = source[i].mParams[j];
+            }
+        }
+    }
+
+    private static int nextStart(String s, int end) {
+        char c;
+
+        while (end < s.length()) {
+            c = s.charAt(end);
+            if (((c - 'A') * (c - 'Z') <= 0) || (((c - 'a') * (c - 'z') <= 0))) {
+                return end;
+            }
+            end++;
+        }
+        return end;
+    }
+
+    private static void addNode(ArrayList<PathDataNode> list, char cmd, float[] val) {
+        list.add(new PathDataNode(cmd, val));
+    }
+
+
+    /**
+     * Parse the floats in the string.
+     * This is an optimized version of parseFloat(s.split(",|\\s"));
+     *
+     * @param s the string containing a command and list of floats
+     * @return array of floats
+     */
+    private static float[] getFloats(String s) {
+        if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') {
+            return new float[0];
+        }
+        try {
+            float[] tmp = new float[s.length()];
+            int count = 0;
+            int pos = 1, end;
+            while ((end = extract(s, pos)) >= 0) {
+                if (pos < end) {
+                    tmp[count++] = Float.parseFloat(s.substring(pos, end));
+                }
+                pos = end + 1;
+            }
+            // handle the final float if there is one
+            if (pos < s.length()) {
+                tmp[count++] = Float.parseFloat(s.substring(pos, s.length()));
+            }
+            return Arrays.copyOf(tmp, count);
+        } catch (NumberFormatException e){
+            Log.e(LOGTAG,"error in parsing \""+s+"\"");
+            throw e;
+        }
+    }
+
+    /**
+     * Calculate the position of the next comma or space
+     * @param s the string to search
+     * @param start the position to start searching
+     * @return the position of the next comma or space or -1 if none found
+     */
+    private static int extract(String s, int start) {
+        int space = s.indexOf(' ', start);
+        int comma = s.indexOf(',', start);
+        if (space == -1) {
+            return comma;
+        }
+        if (comma == -1) {
+            return space;
+        }
+        return (comma > space) ? space : comma;
+    }
+
+    /**
+     * Each PathDataNode represents one command in the "d" attribute of the svg
+     * file.
+     * An array of PathDataNode can represent the whole "d" attribute.
+     */
+    public static class PathDataNode {
+        private char mType;
+        private float[] mParams;
+
+        private PathDataNode(char type, float[] params) {
+            mType = type;
+            mParams = params;
+        }
+
+        private PathDataNode(PathDataNode n) {
+            mType = n.mType;
+            mParams = Arrays.copyOf(n.mParams, n.mParams.length);
+        }
+
+        /**
+         * Convert an array of PathDataNode to Path.
+         *
+         * @param node The source array of PathDataNode.
+         * @param path The target Path object.
+         */
+        public static void nodesToPath(PathDataNode[] node, Path path) {
+            float[] current = new float[4];
+            char previousCommand = 'm';
+            for (int i = 0; i < node.length; i++) {
+                addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
+                previousCommand = node[i].mType;
+            }
+        }
+
+        /**
+         * The current PathDataNode will be interpolated between the
+         * <code>nodeFrom</code> and <code>nodeTo</code> according to the
+         * <code>fraction</code>.
+         *
+         * @param nodeFrom The start value as a PathDataNode.
+         * @param nodeTo The end value as a PathDataNode
+         * @param fraction The fraction to interpolate.
+         */
+        public void interpolatePathDataNode(PathDataNode nodeFrom,
+                PathDataNode nodeTo, float fraction) {
+            for (int i = 0; i < nodeFrom.mParams.length; i++) {
+                mParams[i] = nodeFrom.mParams[i] * (1 - fraction)
+                        + nodeTo.mParams[i] * fraction;
+            }
+        }
+
+        private static void addCommand(Path path, float[] current,
+                char previousCmd, char cmd, float[] val) {
+
+            int incr = 2;
+            float currentX = current[0];
+            float currentY = current[1];
+            float ctrlPointX = current[2];
+            float ctrlPointY = current[3];
+            float reflectiveCtrlPointX;
+            float reflectiveCtrlPointY;
+
+            switch (cmd) {
+                case 'z':
+                case 'Z':
+                    path.close();
+                    return;
+                case 'm':
+                case 'M':
+                case 'l':
+                case 'L':
+                case 't':
+                case 'T':
+                    incr = 2;
+                    break;
+                case 'h':
+                case 'H':
+                case 'v':
+                case 'V':
+                    incr = 1;
+                    break;
+                case 'c':
+                case 'C':
+                    incr = 6;
+                    break;
+                case 's':
+                case 'S':
+                case 'q':
+                case 'Q':
+                    incr = 4;
+                    break;
+                case 'a':
+                case 'A':
+                    incr = 7;
+                    break;
+            }
+            for (int k = 0; k < val.length; k += incr) {
+                switch (cmd) {
+                    case 'm': // moveto - Start a new sub-path (relative)
+                        path.rMoveTo(val[k + 0], val[k + 1]);
+                        currentX += val[k + 0];
+                        currentY += val[k + 1];
+                        break;
+                    case 'M': // moveto - Start a new sub-path
+                        path.moveTo(val[k + 0], val[k + 1]);
+                        currentX = val[k + 0];
+                        currentY = val[k + 1];
+                        break;
+                    case 'l': // lineto - Draw a line from the current point (relative)
+                        path.rLineTo(val[k + 0], val[k + 1]);
+                        currentX += val[k + 0];
+                        currentY += val[k + 1];
+                        break;
+                    case 'L': // lineto - Draw a line from the current point
+                        path.lineTo(val[k + 0], val[k + 1]);
+                        currentX = val[k + 0];
+                        currentY = val[k + 1];
+                        break;
+                    case 'z': // closepath - Close the current subpath
+                    case 'Z': // closepath - Close the current subpath
+                        path.close();
+                        break;
+                    case 'h': // horizontal lineto - Draws a horizontal line (relative)
+                        path.rLineTo(val[k + 0], 0);
+                        currentX += val[k + 0];
+                        break;
+                    case 'H': // horizontal lineto - Draws a horizontal line
+                        path.lineTo(val[k + 0], currentY);
+                        currentX = val[k + 0];
+                        break;
+                    case 'v': // vertical lineto - Draws a vertical line from the current point (r)
+                        path.rLineTo(0, val[k + 0]);
+                        currentY += val[k + 0];
+                        break;
+                    case 'V': // vertical lineto - Draws a vertical line from the current point
+                        path.lineTo(currentX, val[k + 0]);
+                        currentY = val[k + 0];
+                        break;
+                    case 'c': // curveto - Draws a cubic Bézier curve (relative)
+                        path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
+                                val[k + 4], val[k + 5]);
+
+                        ctrlPointX = currentX + val[k + 2];
+                        ctrlPointY = currentY + val[k + 3];
+                        currentX += val[k + 4];
+                        currentY += val[k + 5];
+
+                        break;
+                    case 'C': // curveto - Draws a cubic Bézier curve
+                        path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
+                                val[k + 4], val[k + 5]);
+                        currentX = val[k + 4];
+                        currentY = val[k + 5];
+                        ctrlPointX = val[k + 2];
+                        ctrlPointY = val[k + 3];
+                        break;
+                    case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
+                        reflectiveCtrlPointX = 0;
+                        reflectiveCtrlPointY = 0;
+                        if (previousCmd == 'c' || previousCmd == 's'
+                                || previousCmd == 'C' || previousCmd == 'S') {
+                            reflectiveCtrlPointX = currentX - ctrlPointX;
+                            reflectiveCtrlPointY = currentY - ctrlPointY;
+                        }
+                        path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1],
+                                val[k + 2], val[k + 3]);
+
+                        ctrlPointX = currentX + val[k + 0];
+                        ctrlPointY = currentY + val[k + 1];
+                        currentX += val[k + 2];
+                        currentY += val[k + 3];
+                        break;
+                    case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
+                        reflectiveCtrlPointX = currentX;
+                        reflectiveCtrlPointY = currentY;
+                        if (previousCmd == 'c' || previousCmd == 's'
+                                || previousCmd == 'C' || previousCmd == 'S') {
+                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+                        }
+                        path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+                        ctrlPointX = val[k + 0];
+                        ctrlPointY = val[k + 1];
+                        currentX = val[k + 2];
+                        currentY = val[k + 3];
+                        break;
+                    case 'q': // Draws a quadratic Bézier (relative)
+                        path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+                        ctrlPointX = currentX + val[k + 0];
+                        ctrlPointY = currentY + val[k + 1];
+                        currentX += val[k + 2];
+                        currentY += val[k + 3];
+                        break;
+                    case 'Q': // Draws a quadratic Bézier
+                        path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
+                        ctrlPointX = val[k + 0];
+                        ctrlPointY = val[k + 1];
+                        currentX = val[k + 2];
+                        currentY = val[k + 3];
+                        break;
+                    case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
+                        reflectiveCtrlPointX = 0;
+                        reflectiveCtrlPointY = 0;
+                        if (previousCmd == 'q' || previousCmd == 't'
+                                || previousCmd == 'Q' || previousCmd == 'T') {
+                            reflectiveCtrlPointX = currentX - ctrlPointX;
+                            reflectiveCtrlPointY = currentY - ctrlPointY;
+                        }
+                        path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1]);
+                        ctrlPointX = currentX + reflectiveCtrlPointX;
+                        ctrlPointY = currentY + reflectiveCtrlPointY;
+                        currentX += val[k + 0];
+                        currentY += val[k + 1];
+                        break;
+                    case 'T': // Draws a quadratic Bézier curve (reflective control point)
+                        reflectiveCtrlPointX = currentX;
+                        reflectiveCtrlPointY = currentY;
+                        if (previousCmd == 'q' || previousCmd == 't'
+                                || previousCmd == 'Q' || previousCmd == 'T') {
+                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
+                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
+                        }
+                        path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
+                                val[k + 0], val[k + 1]);
+                        ctrlPointX = reflectiveCtrlPointX;
+                        ctrlPointY = reflectiveCtrlPointY;
+                        currentX = val[k + 0];
+                        currentY = val[k + 1];
+                        break;
+                    case 'a': // Draws an elliptical arc
+                        // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
+                        drawArc(path,
+                                currentX,
+                                currentY,
+                                val[k + 5] + currentX,
+                                val[k + 6] + currentY,
+                                val[k + 0],
+                                val[k + 1],
+                                val[k + 2],
+                                val[k + 3] != 0,
+                                val[k + 4] != 0);
+                        currentX += val[k + 5];
+                        currentY += val[k + 6];
+                        ctrlPointX = currentX;
+                        ctrlPointY = currentY;
+                        break;
+                    case 'A': // Draws an elliptical arc
+                        drawArc(path,
+                                currentX,
+                                currentY,
+                                val[k + 5],
+                                val[k + 6],
+                                val[k + 0],
+                                val[k + 1],
+                                val[k + 2],
+                                val[k + 3] != 0,
+                                val[k + 4] != 0);
+                        currentX = val[k + 5];
+                        currentY = val[k + 6];
+                        ctrlPointX = currentX;
+                        ctrlPointY = currentY;
+                        break;
+                }
+                previousCmd = cmd;
+            }
+            current[0] = currentX;
+            current[1] = currentY;
+            current[2] = ctrlPointX;
+            current[3] = ctrlPointY;
+        }
+
+        private static void drawArc(Path p,
+                float x0,
+                float y0,
+                float x1,
+                float y1,
+                float a,
+                float b,
+                float theta,
+                boolean isMoreThanHalf,
+                boolean isPositiveArc) {
+
+            /* Convert rotation angle from degrees to radians */
+            double thetaD = Math.toRadians(theta);
+            /* Pre-compute rotation matrix entries */
+            double cosTheta = Math.cos(thetaD);
+            double sinTheta = Math.sin(thetaD);
+            /* Transform (x0, y0) and (x1, y1) into unit space */
+            /* using (inverse) rotation, followed by (inverse) scale */
+            double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
+            double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
+            double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
+            double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
+
+            /* Compute differences and averages */
+            double dx = x0p - x1p;
+            double dy = y0p - y1p;
+            double xm = (x0p + x1p) / 2;
+            double ym = (y0p + y1p) / 2;
+            /* Solve for intersecting unit circles */
+            double dsq = dx * dx + dy * dy;
+            if (dsq == 0.0) {
+                Log.w(LOGTAG, " Points are coincident");
+                return; /* Points are coincident */
+            }
+            double disc = 1.0 / dsq - 1.0 / 4.0;
+            if (disc < 0.0) {
+                Log.w(LOGTAG, "Points are too far apart " + dsq);
+                float adjust = (float) (Math.sqrt(dsq) / 1.99999);
+                drawArc(p, x0, y0, x1, y1, a * adjust,
+                        b * adjust, theta, isMoreThanHalf, isPositiveArc);
+                return; /* Points are too far apart */
+            }
+            double s = Math.sqrt(disc);
+            double sdx = s * dx;
+            double sdy = s * dy;
+            double cx;
+            double cy;
+            if (isMoreThanHalf == isPositiveArc) {
+                cx = xm - sdy;
+                cy = ym + sdx;
+            } else {
+                cx = xm + sdy;
+                cy = ym - sdx;
+            }
+
+            double eta0 = Math.atan2((y0p - cy), (x0p - cx));
+
+            double eta1 = Math.atan2((y1p - cy), (x1p - cx));
+
+            double sweep = (eta1 - eta0);
+            if (isPositiveArc != (sweep >= 0)) {
+                if (sweep > 0) {
+                    sweep -= 2 * Math.PI;
+                } else {
+                    sweep += 2 * Math.PI;
+                }
+            }
+
+            cx *= a;
+            cy *= b;
+            double tcx = cx;
+            cx = cx * cosTheta - cy * sinTheta;
+            cy = tcx * sinTheta + cy * cosTheta;
+
+            arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
+        }
+
+        /**
+         * Converts an arc to cubic Bezier segments and records them in p.
+         *
+         * @param p The target for the cubic Bezier segments
+         * @param cx The x coordinate center of the ellipse
+         * @param cy The y coordinate center of the ellipse
+         * @param a The radius of the ellipse in the horizontal direction
+         * @param b The radius of the ellipse in the vertical direction
+         * @param e1x E(eta1) x coordinate of the starting point of the arc
+         * @param e1y E(eta2) y coordinate of the starting point of the arc
+         * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
+         * @param start The start angle of the arc on the ellipse
+         * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
+         */
+        private static void arcToBezier(Path p,
+                double cx,
+                double cy,
+                double a,
+                double b,
+                double e1x,
+                double e1y,
+                double theta,
+                double start,
+                double sweep) {
+            // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
+            // and http://www.spaceroots.org/documents/ellipse/node22.html
+
+            // Maximum of 45 degrees per cubic Bezier segment
+            int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI));
+
+            double eta1 = start;
+            double cosTheta = Math.cos(theta);
+            double sinTheta = Math.sin(theta);
+            double cosEta1 = Math.cos(eta1);
+            double sinEta1 = Math.sin(eta1);
+            double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
+            double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
+
+            double anglePerSegment = sweep / numSegments;
+            for (int i = 0; i < numSegments; i++) {
+                double eta2 = eta1 + anglePerSegment;
+                double sinEta2 = Math.sin(eta2);
+                double cosEta2 = Math.cos(eta2);
+                double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
+                double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
+                double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
+                double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
+                double tanDiff2 = Math.tan((eta2 - eta1) / 2);
+                double alpha =
+                        Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
+                double q1x = e1x + alpha * ep1x;
+                double q1y = e1y + alpha * ep1y;
+                double q2x = e2x - alpha * ep2x;
+                double q2y = e2y - alpha * ep2y;
+
+                p.cubicTo((float) q1x,
+                        (float) q1y,
+                        (float) q2x,
+                        (float) q2y,
+                        (float) e2x,
+                        (float) e2y);
+                eta1 = eta2;
+                e1x = e2x;
+                e1y = e2y;
+                ep1x = ep2x;
+                ep1y = ep2y;
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index d7a913d..76a6f52 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -599,6 +599,40 @@
     }
 
     /**
+     * Gets the app VSYNC offset, in nanoseconds.  This is a positive value indicating
+     * the phase offset of the VSYNC events provided by Choreographer relative to the
+     * display refresh.  For example, if Choreographer reports that the refresh occurred
+     * at time N, it actually occurred at (N - appVsyncOffset).
+     * <p>
+     * Apps generally do not need to be aware of this.  It's only useful for fine-grained
+     * A/V synchronization.
+     */
+    public long getAppVsyncOffsetNanos() {
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            return mDisplayInfo.appVsyncOffsetNanos;
+        }
+    }
+
+    /**
+     * This is how far in advance a buffer must be queued for presentation at
+     * a given time.  If you want a buffer to appear on the screen at
+     * time N, you must submit the buffer before (N - presentationDeadline).
+     * <p>
+     * The desired presentation time for GLES rendering may be set with
+     * {@link android.opengl.EGLExt#eglPresentationTimeANDROID}.  For video decoding, use
+     * {@link android.media.MediaCodec#releaseOutputBuffer(int, long)}.  Times are
+     * expressed in nanoseconds, using the system monotonic clock
+     * ({@link System#nanoTime}).
+     */
+    public long getPresentationDeadlineNanos() {
+        synchronized (this) {
+            updateDisplayInfoLocked();
+            return mDisplayInfo.presentationDeadlineNanos;
+        }
+    }
+
+    /**
      * Gets display metrics that describe the size and density of this display.
      * <p>
      * The size is adjusted based on the current rotation of the display.
diff --git a/core/java/android/view/DisplayInfo.java b/core/java/android/view/DisplayInfo.java
index b0fe0fa..98696c7 100644
--- a/core/java/android/view/DisplayInfo.java
+++ b/core/java/android/view/DisplayInfo.java
@@ -180,6 +180,20 @@
     public float physicalYDpi;
 
     /**
+     * This is a positive value indicating the phase offset of the VSYNC events provided by
+     * Choreographer relative to the display refresh.  For example, if Choreographer reports
+     * that the refresh occurred at time N, it actually occurred at (N - appVsyncOffsetNanos).
+     */
+    public long appVsyncOffsetNanos;
+
+    /**
+     * This is how far in advance a buffer must be queued for presentation at
+     * a given time.  If you want a buffer to appear on the screen at
+     * time N, you must submit the buffer before (N - bufferDeadlineNanos).
+     */
+    public long presentationDeadlineNanos;
+
+    /**
      * The state of the display, such as {@link android.view.Display#STATE_ON}.
      */
     public int state;
@@ -253,6 +267,8 @@
                 && logicalDensityDpi == other.logicalDensityDpi
                 && physicalXDpi == other.physicalXDpi
                 && physicalYDpi == other.physicalYDpi
+                && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+                && presentationDeadlineNanos == other.presentationDeadlineNanos
                 && state == other.state
                 && ownerUid == other.ownerUid
                 && Objects.equal(ownerPackageName, other.ownerPackageName);
@@ -286,6 +302,8 @@
         logicalDensityDpi = other.logicalDensityDpi;
         physicalXDpi = other.physicalXDpi;
         physicalYDpi = other.physicalYDpi;
+        appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+        presentationDeadlineNanos = other.presentationDeadlineNanos;
         state = other.state;
         ownerUid = other.ownerUid;
         ownerPackageName = other.ownerPackageName;
@@ -314,6 +332,8 @@
         logicalDensityDpi = source.readInt();
         physicalXDpi = source.readFloat();
         physicalYDpi = source.readFloat();
+        appVsyncOffsetNanos = source.readLong();
+        presentationDeadlineNanos = source.readLong();
         state = source.readInt();
         ownerUid = source.readInt();
         ownerPackageName = source.readString();
@@ -343,6 +363,8 @@
         dest.writeInt(logicalDensityDpi);
         dest.writeFloat(physicalXDpi);
         dest.writeFloat(physicalYDpi);
+        dest.writeLong(appVsyncOffsetNanos);
+        dest.writeLong(presentationDeadlineNanos);
         dest.writeInt(state);
         dest.writeInt(ownerUid);
         dest.writeString(ownerPackageName);
@@ -450,6 +472,10 @@
         sb.append(physicalYDpi);
         sb.append(") dpi, layerStack ");
         sb.append(layerStack);
+        sb.append(", appVsyncOff ");
+        sb.append(appVsyncOffsetNanos);
+        sb.append(", presDeadline ");
+        sb.append(presentationDeadlineNanos);
         sb.append(", type ");
         sb.append(Display.typeToString(type));
         if (address != null) {
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index 446bbc9..dcd9ba9 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -234,12 +234,11 @@
     protected static native long nFinishRecording(long renderer);
 
     @Override
-    public int drawDisplayList(RenderNode displayList, Rect dirty, int flags) {
-        return nDrawDisplayList(mRenderer, displayList.getNativeDisplayList(),
-                dirty, flags);
+    public int drawRenderNode(RenderNode renderNode, Rect dirty, int flags) {
+        return nDrawRenderNode(mRenderer, renderNode.getNativeDisplayList(), dirty, flags);
     }
 
-    private static native int nDrawDisplayList(long renderer, long displayList,
+    private static native int nDrawRenderNode(long renderer, long renderNode,
             Rect dirty, int flags);
 
     ///////////////////////////////////////////////////////////////////////////
@@ -804,20 +803,12 @@
 
     @Override
     public void drawPicture(Picture picture) {
-        if (picture.createdFromStream) {
-            return;
-        }
-
         picture.endRecording();
         // TODO: Implement rendering
     }
 
     @Override
     public void drawPicture(Picture picture, Rect dst) {
-        if (picture.createdFromStream) {
-            return;
-        }
-
         save();
         translate(dst.left, dst.top);
         if (picture.getWidth() > 0 && picture.getHeight() > 0) {
@@ -829,10 +820,6 @@
 
     @Override
     public void drawPicture(Picture picture, RectF dst) {
-        if (picture.createdFromStream) {
-            return;
-        }
-
         save();
         translate(dst.left, dst.top);
         if (picture.getWidth() > 0 && picture.getHeight() > 0) {
@@ -865,31 +852,7 @@
     private static native void nDrawPoints(long renderer, float[] points,
             int offset, int count, long paint);
 
-    @SuppressWarnings("deprecation")
-    @Override
-    public void drawPosText(char[] text, int index, int count, float[] pos, Paint paint) {
-        if (index < 0 || index + count > text.length || count * 2 > pos.length) {
-            throw new IndexOutOfBoundsException();
-        }
-
-        nDrawPosText(mRenderer, text, index, count, pos, paint.mNativePaint);
-    }
-
-    private static native void nDrawPosText(long renderer, char[] text, int index, int count,
-            float[] pos, long paint);
-
-    @SuppressWarnings("deprecation")
-    @Override
-    public void drawPosText(String text, float[] pos, Paint paint) {
-        if (text.length() * 2 > pos.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-
-        nDrawPosText(mRenderer, text, 0, text.length(), pos, paint.mNativePaint);
-    }
-
-    private static native void nDrawPosText(long renderer, String text, int start, int end,
-            float[] pos, long paint);
+    // Note: drawPosText just uses implementation in Canvas
 
     @Override
     public void drawRect(float left, float top, float right, float bottom, Paint paint) {
@@ -981,22 +944,24 @@
         }
 
         nDrawTextOnPath(mRenderer, text, index, count, path.mNativePath, hOffset, vOffset,
-                paint.mBidiFlags, paint.mNativePaint);
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
 
     private static native void nDrawTextOnPath(long renderer, char[] text, int index, int count,
-            long path, float hOffset, float vOffset, int bidiFlags, long nativePaint);
+            long path, float hOffset, float vOffset, int bidiFlags, long nativePaint,
+            long typeface);
 
     @Override
     public void drawTextOnPath(String text, Path path, float hOffset, float vOffset, Paint paint) {
         if (text.length() == 0) return;
 
         nDrawTextOnPath(mRenderer, text, 0, text.length(), path.mNativePath, hOffset, vOffset,
-                paint.mBidiFlags, paint.mNativePaint);
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
 
     private static native void nDrawTextOnPath(long renderer, String text, int start, int end,
-            long path, float hOffset, float vOffset, int bidiFlags, long nativePaint);
+            long path, float hOffset, float vOffset, int bidiFlags, long nativePaint,
+            long typeface);
 
     @Override
     public void drawTextRun(char[] text, int index, int count, int contextIndex, int contextCount,
diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java
index b8e7d8c..263ebda 100644
--- a/core/java/android/view/HardwareCanvas.java
+++ b/core/java/android/view/HardwareCanvas.java
@@ -61,16 +61,16 @@
      * Draws the specified display list onto this canvas. The display list can only
      * be drawn if {@link android.view.RenderNode#isValid()} returns true.
      *
-     * @param displayList The display list to replay.
+     * @param renderNode The RenderNode to replay.
      */
-    public void drawDisplayList(RenderNode displayList) {
-        drawDisplayList(displayList, null, RenderNode.FLAG_CLIP_CHILDREN);
+    public void drawRenderNode(RenderNode renderNode) {
+        drawRenderNode(renderNode, null, RenderNode.FLAG_CLIP_CHILDREN);
     }
 
     /**
      * Draws the specified display list onto this canvas.
      *
-     * @param displayList The display list to replay.
+     * @param renderNode The RenderNode to replay.
      * @param dirty Ignored, can be null.
      * @param flags Optional flags about drawing, see {@link RenderNode} for
      *              the possible flags.
@@ -80,7 +80,7 @@
      *
      * @hide
      */
-    public abstract int drawDisplayList(RenderNode displayList, Rect dirty, int flags);
+    public abstract int drawRenderNode(RenderNode renderNode, Rect dirty, int flags);
 
     /**
      * Draws the specified layer onto this canvas.
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index c0e42a3..cfb4af2 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -387,17 +387,6 @@
     abstract boolean copyLayerInto(HardwareLayer layer, Bitmap bitmap);
 
     /**
-     * Schedules the functor for execution in either kModeProcess or
-     * kModeProcessNoContext, depending on whether or not there is an EGLContext.
-     *
-     * @param functor The native functor to invoke
-     * @param waitForCompletion If true, this will not return until the functor
-     *                          has invoked. If false, the functor may be invoked
-     *                          asynchronously.
-     */
-    abstract void invokeFunctor(long functor, boolean waitForCompletion);
-
-    /**
      * Initializes the hardware renderer for the specified surface and setup the
      * renderer for drawing, if needed. This is invoked when the ViewAncestor has
      * potentially lost the hardware renderer. The hardware renderer should be
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index b9ed801..577415e 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -311,7 +311,7 @@
         if (mFactory == null) {
             mFactory = mFactory2 = factory;
         } else {
-            mFactory = new FactoryMerger(factory, factory, mFactory, mFactory2);
+            mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);
         }
     }
 
@@ -319,7 +319,11 @@
      * @hide for use by framework
      */
     public void setPrivateFactory(Factory2 factory) {
-        mPrivateFactory = factory;
+        if (mPrivateFactory == null) {
+            mPrivateFactory = factory;
+        } else {
+            mPrivateFactory = new FactoryMerger(factory, factory, mPrivateFactory, mPrivateFactory);
+        }
     }
 
     /**
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 79f19b5..94d8f70 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -458,6 +458,8 @@
         public float xDpi;
         public float yDpi;
         public boolean secure;
+        public long appVsyncOffsetNanos;
+        public long presentationDeadlineNanos;
 
         public PhysicalDisplayInfo() {
         }
@@ -479,7 +481,9 @@
                     && density == other.density
                     && xDpi == other.xDpi
                     && yDpi == other.yDpi
-                    && secure == other.secure;
+                    && secure == other.secure
+                    && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+                    && presentationDeadlineNanos == other.presentationDeadlineNanos;
         }
 
         @Override
@@ -495,6 +499,8 @@
             xDpi = other.xDpi;
             yDpi = other.yDpi;
             secure = other.secure;
+            appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+            presentationDeadlineNanos = other.presentationDeadlineNanos;
         }
 
         // For debugging purposes
@@ -502,7 +508,8 @@
         public String toString() {
             return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, "
                     + "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure
-                    + "}";
+                    + ", appVsyncOffset " + appVsyncOffsetNanos
+                    + ", bufferDeadline " + presentationDeadlineNanos + "}";
         }
     }
 
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 166edc2..bfab654 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -64,9 +64,9 @@
 
     // Keep in sync with DrawFrameTask.h SYNC_* flags
     // Nothing interesting to report
-    private static final int SYNC_OK = 0x0;
+    private static final int SYNC_OK = 0;
     // Needs a ViewRoot invalidate
-    private static final int SYNC_INVALIDATE_REQUIRED = 0x1;
+    private static final int SYNC_INVALIDATE_REQUIRED = 1 << 0;
 
     private static final String[] VISUALIZERS = {
         PROFILE_PROPERTY_VISUALIZE_BARS,
@@ -80,13 +80,13 @@
     private boolean mProfilingEnabled;
 
     ThreadedRenderer(Context context, boolean translucent) {
-        AtlasInitializer.sInstance.init(context);
-
         long rootNodePtr = nCreateRootRenderNode();
         mRootNode = RenderNode.adopt(rootNodePtr);
         mRootNode.setClipToBounds(false);
         mNativeProxy = nCreateProxy(translucent, rootNodePtr);
 
+        AtlasInitializer.sInstance.init(context, mNativeProxy);
+
         // Setup timing
         mChoreographer = Choreographer.getInstance();
         nSetFrameInterval(mNativeProxy, mChoreographer.getFrameIntervalNanos());
@@ -222,7 +222,7 @@
         try {
             canvas.save();
             callbacks.onHardwarePreDraw(canvas);
-            canvas.drawDisplayList(view.getDisplayList());
+            canvas.drawRenderNode(view.getDisplayList());
             callbacks.onHardwarePostDraw(canvas);
             canvas.restore();
         } finally {
@@ -259,9 +259,8 @@
         }
     }
 
-    @Override
-    void invokeFunctor(long functor, boolean waitForCompletion) {
-        nInvokeFunctor(mNativeProxy, functor, waitForCompletion);
+    static void invokeFunctor(long functor, boolean waitForCompletion) {
+        nInvokeFunctor(functor, waitForCompletion);
     }
 
     @Override
@@ -342,7 +341,7 @@
 
         private AtlasInitializer() {}
 
-        synchronized void init(Context context) {
+        synchronized void init(Context context, long renderProxy) {
             if (mInitialized) return;
             IBinder binder = ServiceManager.getService("assetatlas");
             if (binder == null) return;
@@ -356,7 +355,7 @@
                         if (map != null) {
                             // TODO Remove after fixing b/15425820
                             validateMap(context, map);
-                            nSetAtlas(buffer, map);
+                            nSetAtlas(renderProxy, buffer, map);
                             mInitialized = true;
                         }
                         // If IAssetAtlas is not the same class as the IBinder
@@ -399,7 +398,7 @@
 
     static native void setupShadersDiskCache(String cacheFile);
 
-    private static native void nSetAtlas(GraphicBuffer buffer, long[] map);
+    private static native void nSetAtlas(long nativeProxy, GraphicBuffer buffer, long[] map);
 
     private static native long nCreateRootRenderNode();
     private static native long nCreateProxy(boolean translucent, long rootRenderNode);
@@ -419,7 +418,7 @@
     private static native void nRunWithGlContext(long nativeProxy, Runnable runnable);
     private static native void nDestroyCanvasAndSurface(long nativeProxy);
 
-    private static native void nInvokeFunctor(long nativeProxy, long functor, boolean waitForCompletion);
+    private static native void nInvokeFunctor(long functor, boolean waitForCompletion);
 
     private static native long nCreateDisplayListLayer(long nativeProxy, int width, int height);
     private static native long nCreateTextureLayer(long nativeProxy);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index b9e56f3..829e089 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -25,6 +25,7 @@
 import android.annotation.Nullable;
 import android.content.ClipData;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -671,7 +672,7 @@
  * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack
  * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack
  * @attr ref android.R.styleable#View_stateListAnimator
- * @attr ref android.R.styleable#View_viewName
+ * @attr ref android.R.styleable#View_transitionName
  * @attr ref android.R.styleable#View_soundEffectsEnabled
  * @attr ref android.R.styleable#View_tag
  * @attr ref android.R.styleable#View_textAlignment
@@ -3187,20 +3188,23 @@
 
     @ViewDebug.ExportedProperty(deepExport = true, prefix = "bg_")
     private Drawable mBackground;
+    private ColorStateList mBackgroundTint = null;
+    private PorterDuff.Mode mBackgroundTintMode = PorterDuff.Mode.SRC_ATOP;
+    private boolean mHasBackgroundTint = false;
 
     /**
-     * Display list used for backgrounds.
+     * RenderNode used for backgrounds.
      * <p>
      * When non-null and valid, this is expected to contain an up-to-date copy
-     * of the background drawable. It is cleared on temporary detach and reset
+     * of the background drawable. It is cleared on temporary detach, and reset
      * on cleanup.
      */
-    private RenderNode mBackgroundDisplayList;
+    private RenderNode mBackgroundRenderNode;
 
     private int mBackgroundResource;
     private boolean mBackgroundSizeChanged;
 
-    private String mViewName;
+    private String mTransitionName;
 
     static class ListenerInfo {
         /**
@@ -3890,7 +3894,7 @@
                     if (fadingEdge != FADING_EDGE_NONE) {
                         viewFlagValues |= fadingEdge;
                         viewFlagMasks |= FADING_EDGE_MASK;
-                        initializeFadingEdge(a);
+                        initializeFadingEdgeInternal(a);
                     }
                     break;
                 case R.styleable.View_scrollbarStyle:
@@ -4012,8 +4016,8 @@
                 case R.styleable.View_accessibilityLiveRegion:
                     setAccessibilityLiveRegion(a.getInt(attr, ACCESSIBILITY_LIVE_REGION_DEFAULT));
                     break;
-                case R.styleable.View_viewName:
-                    setViewName(a.getString(attr));
+                case R.styleable.View_transitionName:
+                    setTransitionName(a.getString(attr));
                     break;
                 case R.styleable.View_nestedScrollingEnabled:
                     setNestedScrollingEnabled(a.getBoolean(attr, false));
@@ -4022,6 +4026,16 @@
                     setStateListAnimator(AnimatorInflater.loadStateListAnimator(context,
                             a.getResourceId(attr, 0)));
                     break;
+                case R.styleable.View_backgroundTint:
+                    // This will get applied later during setBackground().
+                    mBackgroundTint = a.getColorStateList(R.styleable.View_backgroundTint);
+                    mHasBackgroundTint = true;
+                    break;
+                case R.styleable.View_backgroundTintMode:
+                    // This will get applied later during setBackground().
+                    mBackgroundTintMode = Drawable.parseTintMode(a.getInt(
+                            R.styleable.View_backgroundTintMode, -1), mBackgroundTintMode);
+                    break;
             }
         }
 
@@ -4094,7 +4108,7 @@
         }
 
         if (initializeScrollbars) {
-            initializeScrollbars(a);
+            initializeScrollbarsInternal(a);
         }
 
         a.recycle();
@@ -4222,6 +4236,32 @@
      * @param a the styled attributes set to initialize the fading edges from
      */
     protected void initializeFadingEdge(TypedArray a) {
+        // This method probably shouldn't have been included in the SDK to begin with.
+        // It relies on 'a' having been initialized using an attribute filter array that is
+        // not publicly available to the SDK. The old method has been renamed
+        // to initializeFadingEdgeInternal and hidden for framework use only;
+        // this one initializes using defaults to make it safe to call for apps.
+
+        TypedArray arr = mContext.obtainStyledAttributes(com.android.internal.R.styleable.View);
+
+        initializeFadingEdgeInternal(arr);
+
+        arr.recycle();
+    }
+
+    /**
+     * <p>
+     * Initializes the fading edges from a given set of styled attributes. This
+     * method should be called by subclasses that need fading edges and when an
+     * instance of these subclasses is created programmatically rather than
+     * being inflated from XML. This method is automatically called when the XML
+     * is inflated.
+     * </p>
+     *
+     * @param a the styled attributes set to initialize the fading edges from
+     * @hide This is the real method; the public one is shimmed to be safe to call from apps.
+     */
+    protected void initializeFadingEdgeInternal(TypedArray a) {
         initScrollCache();
 
         mScrollCache.fadingEdgeLength = a.getDimensionPixelSize(
@@ -4336,6 +4376,31 @@
      * @param a the styled attributes set to initialize the scrollbars from
      */
     protected void initializeScrollbars(TypedArray a) {
+        // It's not safe to use this method from apps. The parameter 'a' must have been obtained
+        // using the View filter array which is not available to the SDK. As such, internal
+        // framework usage now uses initializeScrollbarsInternal and we grab a default
+        // TypedArray with the right filter instead here.
+        TypedArray arr = mContext.obtainStyledAttributes(com.android.internal.R.styleable.View);
+
+        initializeScrollbarsInternal(arr);
+
+        // We ignored the method parameter. Recycle the one we actually did use.
+        arr.recycle();
+    }
+
+    /**
+     * <p>
+     * Initializes the scrollbars from a given set of styled attributes. This
+     * method should be called by subclasses that need scrollbars and when an
+     * instance of these subclasses is created programmatically rather than
+     * being inflated from XML. This method is automatically called when the XML
+     * is inflated.
+     * </p>
+     *
+     * @param a the styled attributes set to initialize the scrollbars from
+     * @hide
+     */
+    protected void initializeScrollbarsInternal(TypedArray a) {
         initScrollCache();
 
         final ScrollabilityCache scrollabilityCache = mScrollCache;
@@ -4807,20 +4872,7 @@
 
         final float x = r.exactCenterX();
         final float y = r.exactCenterY();
-        setDrawableHotspot(x, y);
-    }
-
-    /**
-     * Sets the hotspot position for this View's drawables.
-     *
-     * @param x hotspot x coordinate
-     * @param y hotspot y coordinate
-     * @hide
-     */
-    protected void setDrawableHotspot(float x, float y) {
-        if (mBackground != null) {
-            mBackground.setHotspot(x, y);
-        }
+        drawableHotspotChanged(x, y);
     }
 
     /**
@@ -6783,7 +6835,7 @@
      */
     private void setPressed(boolean pressed, float x, float y) {
         if (pressed) {
-            setDrawableHotspot(x, y);
+            drawableHotspotChanged(x, y);
         }
 
         setPressed(pressed);
@@ -9134,7 +9186,7 @@
                     break;
 
                 case MotionEvent.ACTION_MOVE:
-                    setDrawableHotspot(x, y);
+                    drawableHotspotChanged(x, y);
 
                     // Be lenient about moving outside of buttons
                     if (!pointInView(x, y, mTouchSlop)) {
@@ -12957,33 +13009,6 @@
         }
     }
 
-    /**
-     * This method ensures the hardware renderer is in a valid state
-     * before executing the specified action.
-     *
-     * This method will attempt to set a valid state even if the window
-     * the renderer is attached to was destroyed.
-     *
-     * This method is not guaranteed to work. If the hardware renderer
-     * does not exist or cannot be put in a valid state, this method
-     * will not executed the specified action.
-     *
-     * The specified action is executed synchronously.
-     *
-     * @param action The action to execute after the renderer is in a valid state
-     *
-     * @return True if the specified Runnable was executed, false otherwise
-     *
-     * @hide
-     */
-    public boolean executeHardwareAction(Runnable action) {
-        //noinspection SimplifiableIfStatement
-        if (mAttachInfo != null && mAttachInfo.mHardwareRenderer != null) {
-            return mAttachInfo.mHardwareRenderer.safelyRun(action);
-        }
-        return false;
-    }
-
     void invalidateInheritedLayoutMode(int layoutModeOfRoot) {
     }
 
@@ -13432,8 +13457,6 @@
         // Destroy any previous software drawing cache if needed
         if (mLayerType == LAYER_TYPE_SOFTWARE) {
             destroyDrawingCache();
-            invalidateParentCaches();
-            invalidate(true);
         }
 
         mLayerType = layerType;
@@ -13441,13 +13464,10 @@
         mLayerPaint = layerDisabled ? null : (paint == null ? new Paint() : paint);
         mRenderNode.setLayerPaint(mLayerPaint);
 
-        if (mLayerType == LAYER_TYPE_SOFTWARE) {
-            // LAYER_TYPE_SOFTWARE is handled by View:draw(), so need to invalidate()
-            invalidateParentCaches();
-            invalidate(true);
-        } else {
-            invalidateViewProperty(false, false);
-        }
+        // draw() behaves differently if we are on a layer, so we need to
+        // invalidate() here
+        invalidateParentCaches();
+        invalidate(true);
     }
 
     /**
@@ -13759,8 +13779,8 @@
             mRenderNode.destroyDisplayListData();
         }
 
-        if (mBackgroundDisplayList != null && mBackgroundDisplayList.isValid()) {
-            mBackgroundDisplayList.destroyDisplayListData();
+        if (mBackgroundRenderNode != null && mBackgroundRenderNode.isValid()) {
+            mBackgroundRenderNode.destroyDisplayListData();
         }
     }
 
@@ -14244,6 +14264,7 @@
      * @return True if the view is attached to a window and the window is
      *         hardware accelerated; false in any other case.
      */
+    @ViewDebug.ExportedProperty(category = "drawing")
     public boolean isHardwareAccelerated() {
         return mAttachInfo != null && mAttachInfo.mHardwareAccelerated;
     }
@@ -14474,7 +14495,7 @@
             mPrivateFlags &= ~PFLAG_INVALIDATED;
         }
 
-        RenderNode displayList = null;
+        RenderNode renderNode = null;
         Bitmap cache = null;
         boolean hasDisplayList = false;
         if (caching) {
@@ -14509,12 +14530,12 @@
         }
         useDisplayListProperties &= hasDisplayList;
         if (useDisplayListProperties) {
-            displayList = getDisplayList();
-            if (!displayList.isValid()) {
+            renderNode = getDisplayList();
+            if (!renderNode.isValid()) {
                 // Uncommon, but possible. If a view is removed from the hierarchy during the call
                 // to getDisplayList(), the display list will be marked invalid and we should not
                 // try to use it again.
-                displayList = null;
+                renderNode = null;
                 hasDisplayList = false;
                 useDisplayListProperties = false;
             }
@@ -14568,7 +14589,7 @@
                 if (transformToApply != null) {
                     if (concatMatrix) {
                         if (useDisplayListProperties) {
-                            displayList.setAnimationMatrix(transformToApply.getMatrix());
+                            renderNode.setAnimationMatrix(transformToApply.getMatrix());
                         } else {
                             // Undo the scroll translation, apply the transformation matrix,
                             // then redo the scroll translate to get the correct result.
@@ -14611,7 +14632,7 @@
                             layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
                         }
                         if (useDisplayListProperties) {
-                            displayList.setAlpha(alpha * getAlpha() * getTransitionAlpha());
+                            renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha());
                         } else  if (layerType == LAYER_TYPE_NONE) {
                             final int scrollX = hasDisplayList ? 0 : sx;
                             final int scrollY = hasDisplayList ? 0 : sy;
@@ -14643,12 +14664,12 @@
         }
 
         if (!useDisplayListProperties && hasDisplayList) {
-            displayList = getDisplayList();
-            if (!displayList.isValid()) {
+            renderNode = getDisplayList();
+            if (!renderNode.isValid()) {
                 // Uncommon, but possible. If a view is removed from the hierarchy during the call
                 // to getDisplayList(), the display list will be marked invalid and we should not
                 // try to use it again.
-                displayList = null;
+                renderNode = null;
                 hasDisplayList = false;
             }
         }
@@ -14681,7 +14702,7 @@
                     }
                 } else {
                     mPrivateFlags &= ~PFLAG_DIRTY_MASK;
-                    ((HardwareCanvas) canvas).drawDisplayList(displayList, null, flags);
+                    ((HardwareCanvas) canvas).drawRenderNode(renderNode, null, flags);
                 }
             }
         } else if (cache != null) {
@@ -14952,12 +14973,12 @@
         // Attempt to use a display list if requested.
         if (canvas.isHardwareAccelerated() && mAttachInfo != null
                 && mAttachInfo.mHardwareRenderer != null) {
-            mBackgroundDisplayList = getDrawableDisplayList(background, mBackgroundDisplayList);
+            mBackgroundRenderNode = getDrawableRenderNode(background, mBackgroundRenderNode);
 
-            final RenderNode displayList = mBackgroundDisplayList;
+            final RenderNode displayList = mBackgroundRenderNode;
             if (displayList != null && displayList.isValid()) {
                 setBackgroundDisplayListProperties(displayList);
-                ((HardwareCanvas) canvas).drawDisplayList(displayList);
+                ((HardwareCanvas) canvas).drawRenderNode(displayList);
                 return;
             }
         }
@@ -14988,30 +15009,30 @@
      * specified Drawable.
      *
      * @param drawable Drawable for which to create a display list
-     * @param displayList Existing display list, or {@code null}
+     * @param renderNode Existing RenderNode, or {@code null}
      * @return A valid display list for the specified drawable
      */
-    private RenderNode getDrawableDisplayList(Drawable drawable, RenderNode displayList) {
-        if (displayList == null) {
-            displayList = RenderNode.create(drawable.getClass().getName());
+    private RenderNode getDrawableRenderNode(Drawable drawable, RenderNode renderNode) {
+        if (renderNode == null) {
+            renderNode = RenderNode.create(drawable.getClass().getName());
         }
 
         final Rect bounds = drawable.getBounds();
         final int width = bounds.width();
         final int height = bounds.height();
-        final HardwareCanvas canvas = displayList.start(width, height);
+        final HardwareCanvas canvas = renderNode.start(width, height);
         try {
             drawable.draw(canvas);
         } finally {
-            displayList.end(canvas);
+            renderNode.end(canvas);
         }
 
         // Set up drawable properties that are view-independent.
-        displayList.setLeftTopRightBottom(bounds.left, bounds.top, bounds.right, bounds.bottom);
-        displayList.setProjectBackwards(drawable.isProjected());
-        displayList.setProjectionReceiver(true);
-        displayList.setClipToBounds(false);
-        return displayList;
+        renderNode.setLeftTopRightBottom(bounds.left, bounds.top, bounds.right, bounds.bottom);
+        renderNode.setProjectBackwards(drawable.isProjected());
+        renderNode.setProjectionReceiver(true);
+        renderNode.setClipToBounds(false);
+        return renderNode;
     }
 
     /**
@@ -15521,6 +15542,20 @@
     }
 
     /**
+     * This function is called whenever the drawable hotspot changes.
+     * <p>
+     * Be sure to call through to the superclass when overriding this function.
+     *
+     * @param x hotspot x coordinate
+     * @param y hotspot y coordinate
+     */
+    public void drawableHotspotChanged(float x, float y) {
+        if (mBackground != null) {
+            mBackground.setHotspot(x, y);
+        }
+    }
+
+    /**
      * Call this to force a view to update its drawable state. This will cause
      * drawableStateChanged to be called on this view. Views that are interested
      * in the new state should call getDrawableState.
@@ -15703,7 +15738,7 @@
             return;
         }
 
-        Drawable d= null;
+        Drawable d = null;
         if (resid != 0) {
             d = mContext.getDrawable(resid);
         }
@@ -15779,8 +15814,9 @@
 
             // Compare the minimum sizes of the old Drawable and the new.  If there isn't an old or
             // if it has a different minimum size, we should layout again
-            if (mBackground == null || mBackground.getMinimumHeight() != background.getMinimumHeight() ||
-                    mBackground.getMinimumWidth() != background.getMinimumWidth()) {
+            if (mBackground == null
+                    || mBackground.getMinimumHeight() != background.getMinimumHeight()
+                    || mBackground.getMinimumWidth() != background.getMinimumWidth()) {
                 requestLayout = true;
             }
 
@@ -15791,6 +15827,8 @@
             background.setVisible(getVisibility() == VISIBLE, false);
             mBackground = background;
 
+            applyBackgroundTint();
+
             if ((mPrivateFlags & PFLAG_SKIP_DRAW) != 0) {
                 mPrivateFlags &= ~PFLAG_SKIP_DRAW;
                 mPrivateFlags |= PFLAG_ONLY_DRAWS_BACKGROUND;
@@ -15846,6 +15884,88 @@
     }
 
     /**
+     * Applies a tint to the background drawable.
+     * <p>
+     * Subsequent calls to {@link #setBackground(Drawable)} will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#View_backgroundTint
+     * @attr ref android.R.styleable#View_backgroundTintMode
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+     */
+    private void setBackgroundTint(@Nullable ColorStateList tint,
+            @Nullable PorterDuff.Mode tintMode) {
+        mBackgroundTint = tint;
+        mBackgroundTintMode = tintMode;
+        mHasBackgroundTint = true;
+
+        applyBackgroundTint();
+    }
+
+    /**
+     * Applies a tint to the background drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+     * <p>
+     * Subsequent calls to {@link #setBackground(Drawable)} will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#View_backgroundTint
+     * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+     */
+    public void setBackgroundTint(@Nullable ColorStateList tint) {
+        setBackgroundTint(tint, mBackgroundTintMode);
+    }
+
+    /**
+     * @return the tint applied to the background drawable
+     * @attr ref android.R.styleable#View_backgroundTint
+     * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public ColorStateList getBackgroundTint() {
+        return mBackgroundTint;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setBackgroundTint(ColorStateList)}} to the background drawable.
+     * The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#View_backgroundTintMode
+     * @see #setBackgroundTint(ColorStateList)
+     */
+    public void setBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+        setBackgroundTint(mBackgroundTint, tintMode);
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the background drawable
+     * @attr ref android.R.styleable#View_backgroundTintMode
+     * @see #setBackgroundTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getBackgroundTintMode() {
+        return mBackgroundTintMode;
+    }
+
+    private void applyBackgroundTint() {
+        if (mBackground != null && mHasBackgroundTint) {
+            mBackground = mBackground.mutate();
+            mBackground.setTint(mBackgroundTint, mBackgroundTintMode);
+        }
+    }
+
+    /**
      * Sets the padding. The view may add on the space required to display
      * the scrollbars, depending on the style and visibility of the scrollbars.
      * So the values returned from {@link #getPaddingLeft}, {@link #getPaddingTop},
@@ -18820,15 +18940,15 @@
     }
 
     /**
-     * Adds all Views that have {@link #getViewName()} non-null to namedElements.
-     * @param namedElements Will contain all Views in the hierarchy having a view name.
+     * Adds all Views that have {@link #getTransitionName()} non-null to namedElements.
+     * @param namedElements Will contain all Views in the hierarchy having a transitionName.
      * @hide
      */
     public void findNamedViews(Map<String, View> namedElements) {
         if (getVisibility() == VISIBLE) {
-            String viewName = getViewName();
-            if (viewName != null) {
-                namedElements.put(viewName, this);
+            String transitionName = getTransitionName();
+            if (transitionName != null) {
+                namedElements.put(transitionName, this);
             }
         }
     }
@@ -19230,10 +19350,18 @@
      * Sets the name of the View to be used to identify Views in Transitions.
      * Names should be unique in the View hierarchy.
      *
-     * @param viewName The name of the View to uniquely identify it for Transitions.
+     * @param transitionName The name of the View to uniquely identify it for Transitions.
      */
-    public final void setViewName(String viewName) {
-        mViewName = viewName;
+    public final void setTransitionName(String transitionName) {
+        mTransitionName = transitionName;
+    }
+
+    /**
+     * To be removed before L release.
+     * @hide
+     */
+    public final void setViewName(String transitionName) {
+        setTransitionName(transitionName);
     }
 
     /**
@@ -19245,11 +19373,17 @@
      * @return The name used of the View to be used to identify Views in Transitions or null
      * if no name has been given.
      */
-    public String getViewName() {
-        return mViewName;
+    public String getTransitionName() {
+        return mTransitionName;
     }
 
     /**
+     * To be removed before L release.
+     * @hide
+     */
+    public String getViewName() { return getTransitionName(); }
+
+    /**
      * Interface definition for a callback to be invoked when a hardware key event is
      * dispatched to this view. The callback will be invoked before the key event is
      * given to the view. This is only useful for hardware keyboards; a software input
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index a02e76b..45ac073 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -2326,13 +2326,13 @@
      * individually during the transition.
      * @return True if the ViewGroup should be acted on together during an Activity transition.
      * The default value is false when the background is null and true when the background
-     * is not null or if {@link #getViewName()} is not null.
+     * is not null or if {@link #getTransitionName()} is not null.
      */
     public boolean isTransitionGroup() {
         if ((mGroupFlags & FLAG_IS_TRANSITION_GROUP_SET) != 0) {
             return ((mGroupFlags & FLAG_IS_TRANSITION_GROUP) != 0);
         } else {
-            return getBackground() != null || getViewName() != null;
+            return getBackground() != null || getTransitionName() != null;
         }
     }
 
@@ -4386,7 +4386,7 @@
             // Make sure we do not set both flags at the same time
             int opaqueFlag = isOpaque ? PFLAG_DIRTY_OPAQUE : PFLAG_DIRTY;
 
-            if (child.mLayerType == LAYER_TYPE_SOFTWARE) {
+            if (child.mLayerType != LAYER_TYPE_NONE) {
                 mPrivateFlags |= PFLAG_INVALIDATED;
                 mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
             }
@@ -4497,7 +4497,7 @@
                 location[CHILD_LEFT_INDEX] = left;
                 location[CHILD_TOP_INDEX] = top;
 
-                if (mLayerType == LAYER_TYPE_SOFTWARE) {
+                if (mLayerType != LAYER_TYPE_NONE) {
                     mPrivateFlags |= PFLAG_INVALIDATED;
                 }
 
@@ -4515,7 +4515,7 @@
                     dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
                 }
 
-                if (mLayerType == LAYER_TYPE_SOFTWARE) {
+                if (mLayerType != LAYER_TYPE_NONE) {
                     mPrivateFlags |= PFLAG_INVALIDATED;
                 }
 
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index af1de78..3f72b4c 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -52,7 +52,7 @@
      * The View whose properties are being animated by this class. This is set at
      * construction time.
      */
-    private final View mView;
+    final View mView;
 
     /**
      * The duration of the underlying Animator object. By default, we don't set the duration
@@ -253,10 +253,9 @@
     ViewPropertyAnimator(View view) {
         mView = view;
         view.ensureTransformationInfo();
-        // TODO: Disabled because of b/15287046
-        //if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
-        //    mRTBackend = new ViewPropertyAnimatorRT(view);
-        //}
+        if (view.getContext().getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.L) {
+            mRTBackend = new ViewPropertyAnimatorRT(view);
+        }
     }
 
     /**
@@ -434,6 +433,9 @@
         }
         mPendingAnimations.clear();
         mView.removeCallbacks(mAnimationStarter);
+        if (mRTBackend != null) {
+            mRTBackend.cancelAll();
+        }
     }
 
     /**
diff --git a/core/java/android/view/ViewPropertyAnimatorRT.java b/core/java/android/view/ViewPropertyAnimatorRT.java
index 709efdb..8b4277a 100644
--- a/core/java/android/view/ViewPropertyAnimatorRT.java
+++ b/core/java/android/view/ViewPropertyAnimatorRT.java
@@ -50,6 +50,15 @@
         return true;
     }
 
+    public void cancelAll() {
+        for (int i = 0; i < mAnimators.length; i++) {
+            if (mAnimators[i] != null) {
+                mAnimators[i].cancel();
+                mAnimators[i] = null;
+            }
+        }
+    }
+
     private void doStartAnimation(ViewPropertyAnimator parent) {
         int size = parent.mPendingAnimations.size();
 
@@ -63,12 +72,22 @@
             NameValuesHolder holder = parent.mPendingAnimations.get(i);
             int property = RenderNodeAnimator.mapViewPropertyToRenderProperty(holder.mNameConstant);
 
-            RenderNodeAnimator animator = new RenderNodeAnimator(property, holder.mFromValue + holder.mDeltaValue);
+            final float finalValue = holder.mFromValue + holder.mDeltaValue;
+            RenderNodeAnimator animator = new RenderNodeAnimator(property, finalValue);
             animator.setStartDelay(startDelay);
             animator.setDuration(duration);
             animator.setInterpolator(interpolator);
             animator.setTarget(mView);
             animator.start();
+
+            // Alpha is a special snowflake that has the canonical value stored
+            // in mTransformationInfo instead of in RenderNode, so we need to update
+            // it with the final value here.
+            if (property == RenderNodeAnimator.ALPHA) {
+                // Don't need null check because ViewPropertyAnimator's
+                // ctor calls ensureTransformationInfo()
+                parent.mView.mTransformationInfo.mAlpha = finalValue;
+            }
         }
 
         parent.mPendingAnimations.clear();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 82e5ddd..84e30c6 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -41,6 +41,7 @@
 import android.hardware.display.DisplayManager.DisplayListener;
 import android.media.AudioManager;
 import android.os.Binder;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Debug;
 import android.os.Handler;
@@ -119,6 +120,9 @@
     private static final String PROPERTY_PROFILE_RENDERING = "viewroot.profile_rendering";
     private static final String PROPERTY_MEDIA_DISABLED = "config.disable_media";
 
+    // property used by emulator to determine display shape
+    private static final String PROPERTY_EMULATOR_CIRCULAR = "ro.emulator.circular";
+
     /**
      * Maximum time we allow the user to roll the trackball enough to generate
      * a key event, before resetting the counters.
@@ -325,6 +329,8 @@
     /** Set to true once doDie() has been called. */
     private boolean mRemoved;
 
+    private boolean mIsEmulator;
+
     /**
      * Consistency verifier for debugging purposes.
      */
@@ -655,12 +661,17 @@
         }
     }
 
-    public boolean invokeFunctor(long functor, boolean waitForCompletion) {
-        if (mAttachInfo.mHardwareRenderer == null) {
-            return false;
-        }
-        mAttachInfo.mHardwareRenderer.invokeFunctor(functor, waitForCompletion);
-        return true;
+    /**
+     * Schedules the functor for execution in either kModeProcess or
+     * kModeProcessNoContext, depending on whether or not there is an EGLContext.
+     *
+     * @param functor The native functor to invoke
+     * @param waitForCompletion If true, this will not return until the functor
+     *                          has invoked. If false, the functor may be invoked
+     *                          asynchronously.
+     */
+    public void invokeFunctor(long functor, boolean waitForCompletion) {
+        ThreadedRenderer.invokeFunctor(functor, waitForCompletion);
     }
 
     private void enableHardwareAcceleration(WindowManager.LayoutParams attrs) {
@@ -1170,8 +1181,9 @@
         if ((mWindowAttributes.flags & WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN) != 0
                 && mDisplay.getDisplayId() == 0) {
             // we're fullscreen and not hosted in an ActivityView
-            isRound = mContext.getResources().getBoolean(
-                    com.android.internal.R.bool.config_windowIsRound);
+            isRound = (mIsEmulator && SystemProperties.getBoolean(PROPERTY_EMULATOR_CIRCULAR, false))
+                    || mContext.getResources().getBoolean(
+                            com.android.internal.R.bool.config_windowIsRound);
         }
         host.dispatchApplyWindowInsets(new WindowInsets(
                 mFitSystemWindowsInsets, isRound));
@@ -5426,6 +5438,9 @@
                         mHandler.sendEmptyMessageDelayed(MSG_INVALIDATE_WORLD, 200);
                     }
                 }
+
+                // detect emulator
+                mIsEmulator = Build.HARDWARE.contains("goldfish");
             }
         });
     }
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index a92bf59..bf5c84e 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -167,5 +167,5 @@
      * Invalidate all visible windows. Then report back on the callback once all windows have
      * redrawn.
      */
-    public abstract void waitForAllWindowsDrawn(IRemoteCallback callback, long timeout);
+    public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout);
 }
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index d426edc..024600d 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1154,6 +1154,12 @@
     public void setLastInputMethodWindowLw(WindowState ime, WindowState target);
 
     /**
+     * Show the recents task list app.
+     * @hide
+     */
+    public void showRecentApps();
+
+    /**
      * @return The current height of the input method window.
      */
     public int getInputMethodWindowVisibleHeightLw();
diff --git a/core/java/android/view/animation/PathInterpolator.java b/core/java/android/view/animation/PathInterpolator.java
index da12ffb..945ecf0 100644
--- a/core/java/android/view/animation/PathInterpolator.java
+++ b/core/java/android/view/animation/PathInterpolator.java
@@ -21,6 +21,7 @@
 import android.content.res.TypedArray;
 import android.graphics.Path;
 import android.util.AttributeSet;
+import android.util.PathParser;
 import android.view.InflateException;
 
 import com.android.internal.R;
@@ -102,28 +103,40 @@
     }
 
     private void parseInterpolatorFromTypeArray(TypedArray a) {
-        if (!a.hasValue(R.styleable.PathInterpolator_controlX1)) {
-            throw new InflateException("pathInterpolator requires the controlX1 attribute");
-        } else if (!a.hasValue(R.styleable.PathInterpolator_controlY1)) {
-            throw new InflateException("pathInterpolator requires the controlY1 attribute");
-        }
-        float x1 = a.getFloat(R.styleable.PathInterpolator_controlX1, 0);
-        float y1 = a.getFloat(R.styleable.PathInterpolator_controlY1, 0);
-
-        boolean hasX2 = a.hasValue(R.styleable.PathInterpolator_controlX2);
-        boolean hasY2 = a.hasValue(R.styleable.PathInterpolator_controlY2);
-
-        if (hasX2 != hasY2) {
-            throw new InflateException(
-                    "pathInterpolator requires both controlX2 and controlY2 for cubic Beziers.");
-        }
-
-        if (!hasX2) {
-            initQuad(x1, y1);
+        // If there is pathData defined in the xml file, then the controls points
+        // will be all coming from pathData.
+        if (a.hasValue(R.styleable.PathInterpolator_pathData)) {
+            String pathData = a.getString(R.styleable.PathInterpolator_pathData);
+            Path path = PathParser.createPathFromPathData(pathData);
+            if (path == null) {
+                throw new InflateException("The path is null, which is created"
+                        + " from " + pathData);
+            }
+            initPath(path);
         } else {
-            float x2 = a.getFloat(R.styleable.PathInterpolator_controlX2, 0);
-            float y2 = a.getFloat(R.styleable.PathInterpolator_controlY2, 0);
-            initCubic(x1, y1, x2, y2);
+            if (!a.hasValue(R.styleable.PathInterpolator_controlX1)) {
+                throw new InflateException("pathInterpolator requires the controlX1 attribute");
+            } else if (!a.hasValue(R.styleable.PathInterpolator_controlY1)) {
+                throw new InflateException("pathInterpolator requires the controlY1 attribute");
+            }
+            float x1 = a.getFloat(R.styleable.PathInterpolator_controlX1, 0);
+            float y1 = a.getFloat(R.styleable.PathInterpolator_controlY1, 0);
+
+            boolean hasX2 = a.hasValue(R.styleable.PathInterpolator_controlX2);
+            boolean hasY2 = a.hasValue(R.styleable.PathInterpolator_controlY2);
+
+            if (hasX2 != hasY2) {
+                throw new InflateException(
+                        "pathInterpolator requires both controlX2 and controlY2 for cubic Beziers.");
+            }
+
+            if (!hasX2) {
+                initQuad(x1, y1);
+            } else {
+                float x2 = a.getFloat(R.styleable.PathInterpolator_controlX2, 0);
+                float y2 = a.getFloat(R.styleable.PathInterpolator_controlY2, 0);
+                initCubic(x1, y1, x2, y2);
+            }
         }
     }
 
@@ -216,5 +229,4 @@
         float endY = mY[endIndex];
         return startY + (fraction * (endY - startY));
     }
-
 }
diff --git a/core/java/android/webkit/PermissionRequest.java b/core/java/android/webkit/PermissionRequest.java
index fa760b7..231bf2d 100644
--- a/core/java/android/webkit/PermissionRequest.java
+++ b/core/java/android/webkit/PermissionRequest.java
@@ -39,6 +39,12 @@
      * Resource belongs to audio capture device, like microphone.
      */
     public final static long RESOURCE_AUDIO_CAPTURE = 1 << 2;
+    /**
+     * Resource belongs to protected media identifier.
+     * After the user grants this resource, the origin can use EME APIs to generate the license
+     * requests.
+     */
+    public final static long RESOURCE_PROTECTED_MEDIA_ID = 1 << 3;
 
     /**
      * @return the origin of web content which attempt to access the restricted
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 372228c..f2692da 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -785,7 +785,7 @@
 
         setVerticalScrollBarEnabled(true);
         TypedArray a = context.obtainStyledAttributes(R.styleable.View);
-        initializeScrollbars(a);
+        initializeScrollbarsInternal(a);
         a.recycle();
     }
 
diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java
index 08931fe..7e2d809 100644
--- a/core/java/android/widget/AbsSeekBar.java
+++ b/core/java/android/widget/AbsSeekBar.java
@@ -16,10 +16,13 @@
 
 package android.widget;
 
+import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.Insets;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region.Op;
 import android.graphics.drawable.Drawable;
@@ -31,10 +34,16 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityNodeInfo;
 
+import com.android.internal.R;
+
 public abstract class AbsSeekBar extends ProgressBar {
     private final Rect mTempRect = new Rect();
 
     private Drawable mThumb;
+    private ColorStateList mThumbTint = null;
+    private PorterDuff.Mode mThumbTintMode = PorterDuff.Mode.SRC_ATOP;
+    private boolean mHasThumbTint = false;
+
     private int mThumbOffset;
     private boolean mSplitTrack;
 
@@ -83,6 +92,15 @@
         final Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb);
         setThumb(thumb);
 
+        if (a.hasValue(R.styleable.SeekBar_thumbTint)) {
+            mThumbTint = a.getColorStateList(R.styleable.SeekBar_thumbTint);
+            mThumbTintMode = Drawable.parseTintMode(a.getInt(
+                    R.styleable.SeekBar_thumbTintMode, -1), mThumbTintMode);
+            mHasThumbTint = true;
+
+            applyThumbTint();
+        }
+
         // Guess thumb offset if thumb != null, but allow layout to override.
         final int thumbOffset = a.getDimensionPixelOffset(
                 com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset());
@@ -108,7 +126,7 @@
      * @param thumb Drawable representing the thumb
      */
     public void setThumb(Drawable thumb) {
-        boolean needUpdate;
+        final boolean needUpdate;
         // This way, calling setThumb again with the same bitmap will result in
         // it recalcuating mThumbOffset (if for example it the bounds of the
         // drawable changed)
@@ -118,6 +136,7 @@
         } else {
             needUpdate = false;
         }
+
         if (thumb != null) {
             thumb.setCallback(this);
             if (canResolveLayoutDirection()) {
@@ -136,8 +155,12 @@
                 requestLayout();
             }
         }
+
         mThumb = thumb;
+
+        applyThumbTint();
         invalidate();
+
         if (needUpdate) {
             updateThumbAndTrackPos(getWidth(), getHeight());
             if (thumb != null && thumb.isStateful()) {
@@ -160,6 +183,88 @@
     }
 
     /**
+     * Applies a tint to the thumb drawable.
+     * <p>
+     * Subsequent calls to {@link #setThumb(Drawable)} will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#SeekBar_thumbTint
+     * @attr ref android.R.styleable#SeekBar_thumbTintMode
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+     */
+    private void setThumbTint(@Nullable ColorStateList tint,
+            @Nullable PorterDuff.Mode tintMode) {
+        mThumbTint = tint;
+        mThumbTintMode = tintMode;
+        mHasThumbTint = true;
+
+        applyThumbTint();
+    }
+
+    /**
+     * Applies a tint to the thumb drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+     * <p>
+     * Subsequent calls to {@link #setThumb(Drawable)} will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#SeekBar_thumbTint
+     * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+     */
+    public void setThumbTint(@Nullable ColorStateList tint) {
+        setThumbTint(tint, mThumbTintMode);
+    }
+
+    /**
+     * @return the tint applied to the thumb drawable
+     * @attr ref android.R.styleable#SeekBar_thumbTint
+     * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public ColorStateList getThumbTint() {
+        return mThumbTint;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setThumbTint(ColorStateList)}} to the thumb drawable. The
+     * default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#SeekBar_thumbTintMode
+     * @see #setThumbTint(ColorStateList)
+     */
+    public void setThumbTintMode(@Nullable PorterDuff.Mode tintMode) {
+        setThumbTint(mThumbTint, tintMode);
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the thumb drawable
+     * @attr ref android.R.styleable#SeekBar_thumbTintMode
+     * @see #setThumbTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getThumbTintMode() {
+        return mThumbTintMode;
+    }
+
+    private void applyThumbTint() {
+        if (mThumb != null && mHasThumbTint) {
+            mThumb = mThumb.mutate();
+            mThumb.setTint(mThumbTint, mThumbTintMode);
+        }
+    }
+
+    /**
      * @see #setThumbOffset(int)
      */
     public int getThumbOffset() {
@@ -237,7 +342,10 @@
     @Override
     public void jumpDrawablesToCurrentState() {
         super.jumpDrawablesToCurrentState();
-        if (mThumb != null) mThumb.jumpToCurrentState();
+
+        if (mThumb != null) {
+            mThumb.jumpToCurrentState();
+        }
     }
 
     @Override
@@ -255,29 +363,12 @@
         }
     }
 
-    /** @hide */
     @Override
-    protected void setDrawableHotspot(float x, float y) {
-        super.setDrawableHotspot(x, y);
+    public void drawableHotspotChanged(float x, float y) {
+        super.drawableHotspotChanged(x, y);
 
-        final Drawable progressDrawable = getProgressDrawable();
-        if (progressDrawable != null) {
-            progressDrawable.setHotspot(x, y);
-        }
-
-        final Drawable thumb = mThumb;
-        if (thumb != null) {
-            thumb.setHotspot(x, y);
-        }
-    }
-
-    @Override
-    public void invalidateDrawable(Drawable dr) {
-        super.invalidateDrawable(dr);
-
-        if (dr == mThumb) {
-            // Handle changes to thumb width and height.
-            requestLayout();
+        if (mThumb != null) {
+            mThumb.setHotspot(x, y);
         }
     }
 
@@ -373,11 +464,11 @@
 
         final Drawable background = getBackground();
         if (background != null) {
-            final Rect bounds = mThumb.getBounds();
+            final Rect bounds = thumb.getBounds();
             final int offsetX = mPaddingLeft - mThumbOffset;
             final int offsetY = mPaddingTop;
-            background.setHotspotBounds(left + offsetX, bounds.top + offsetY,
-                    right + offsetX, bounds.bottom + offsetY);
+            background.setHotspotBounds(left + offsetX, top + offsetY,
+                    right + offsetX, bottom + offsetY);
         }
 
         // Canvas will be translated, so 0,0 is where we start drawing
@@ -399,8 +490,8 @@
     @Override
     protected synchronized void onDraw(Canvas canvas) {
         super.onDraw(canvas);
-
         drawThumb(canvas);
+
     }
 
     @Override
diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java
index 7113793..4aa2300 100644
--- a/core/java/android/widget/CheckedTextView.java
+++ b/core/java/android/widget/CheckedTextView.java
@@ -307,10 +307,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    protected void setDrawableHotspot(float x, float y) {
-        super.setDrawableHotspot(x, y);
+    public void drawableHotspotChanged(float x, float y) {
+        super.drawableHotspotChanged(x, y);
 
         if (mCheckMarkDrawable != null) {
             mCheckMarkDrawable.setHotspot(x, y);
diff --git a/core/java/android/widget/CompoundButton.java b/core/java/android/widget/CompoundButton.java
index c934ad7..9ba0fe1 100644
--- a/core/java/android/widget/CompoundButton.java
+++ b/core/java/android/widget/CompoundButton.java
@@ -16,9 +16,12 @@
 
 package android.widget;
 
+import android.annotation.Nullable;
+import android.graphics.PorterDuff;
 import com.android.internal.R;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
@@ -48,7 +51,12 @@
     private boolean mChecked;
     private int mButtonResource;
     private boolean mBroadcasting;
+
     private Drawable mButtonDrawable;
+    private ColorStateList mButtonTint = null;
+    private PorterDuff.Mode mButtonTintMode = PorterDuff.Mode.SRC_ATOP;
+    private boolean mHasButtonTint = false;
+
     private OnCheckedChangeListener mOnCheckedChangeListener;
     private OnCheckedChangeListener mOnCheckedChangeWidgetListener;
 
@@ -74,13 +82,22 @@
         final TypedArray a = context.obtainStyledAttributes(
                 attrs, com.android.internal.R.styleable.CompoundButton, defStyleAttr, defStyleRes);
 
-        Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button);
+        final Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button);
         if (d != null) {
             setButtonDrawable(d);
         }
 
-        boolean checked = a
-                .getBoolean(com.android.internal.R.styleable.CompoundButton_checked, false);
+        if (a.hasValue(R.styleable.CompoundButton_buttonTint)) {
+            mButtonTint = a.getColorStateList(R.styleable.CompoundButton_buttonTint);
+            mButtonTintMode = Drawable.parseTintMode(a.getInt(
+                    R.styleable.CompoundButton_buttonTintMode, -1), mButtonTintMode);
+            mHasButtonTint = true;
+
+            applyButtonTint();
+        }
+
+        final boolean checked = a.getBoolean(
+                com.android.internal.R.styleable.CompoundButton_checked, false);
         setChecked(checked);
 
         a.recycle();
@@ -173,9 +190,11 @@
     }
 
     /**
-     * Set the background to a given Drawable, identified by its resource id.
+     * Set the button graphic to a given Drawable, identified by its resource
+     * id.
      *
-     * @param resid the resource id of the drawable to use as the background 
+     * @param resid the resource id of the drawable to use as the button
+     *        graphic
      */
     public void setButtonDrawable(int resid) {
         if (resid != 0 && resid == mButtonResource) {
@@ -192,23 +211,114 @@
     }
 
     /**
-     * Set the background to a given Drawable
+     * Set the button graphic to a given Drawable
      *
-     * @param d The Drawable to use as the background
+     * @param d The Drawable to use as the button graphic
      */
     public void setButtonDrawable(Drawable d) {
-        if (d != null) {
+        if (mButtonDrawable != d) {
             if (mButtonDrawable != null) {
                 mButtonDrawable.setCallback(null);
                 unscheduleDrawable(mButtonDrawable);
             }
-            d.setCallback(this);
-            d.setVisible(getVisibility() == VISIBLE, false);
-            mButtonDrawable = d;
-            setMinHeight(mButtonDrawable.getIntrinsicHeight());
-        }
 
-        refreshDrawableState();
+            mButtonDrawable = d;
+
+            if (d != null) {
+                d.setCallback(this);
+                d.setLayoutDirection(getLayoutDirection());
+                if (d.isStateful()) {
+                    d.setState(getDrawableState());
+                }
+                d.setVisible(getVisibility() == VISIBLE, false);
+                setMinHeight(d.getIntrinsicHeight());
+                applyButtonTint();
+            }
+        }
+    }
+
+    /**
+     * Applies a tint to the button drawable.
+     * <p>
+     * Subsequent calls to {@link #setButtonDrawable(Drawable)} will
+     * automatically mutate the drawable and apply the specified tint and tint
+     * mode using
+     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#CompoundButton_buttonTint
+     * @attr ref android.R.styleable#CompoundButton_buttonTintMode
+     * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+     */
+    private void setButtonTint(@Nullable ColorStateList tint,
+            @Nullable PorterDuff.Mode tintMode) {
+        mButtonTint = tint;
+        mButtonTintMode = tintMode;
+        mHasButtonTint = true;
+
+        applyButtonTint();
+    }
+
+    /**
+     * Applies a tint to the button drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+     * <p>
+     * Subsequent calls to {@link #setButtonDrawable(Drawable)} will
+     * automatically mutate the drawable and apply the specified tint and tint
+     * mode using
+     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#CompoundButton_buttonTint
+     * @see #setButtonTint(ColorStateList, android.graphics.PorterDuff.Mode)
+     */
+    public void setButtonTint(@Nullable ColorStateList tint) {
+        setButtonTint(tint, mButtonTintMode);
+    }
+
+    /**
+     * @return the tint applied to the button drawable
+     * @attr ref android.R.styleable#CompoundButton_buttonTint
+     * @see #setButtonTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public ColorStateList getButtonTint() {
+        return mButtonTint;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setButtonTint(ColorStateList)}} to the button drawable. The
+     * default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#CompoundButton_buttonTintMode
+     * @see #setButtonTint(ColorStateList)
+     */
+    public void setButtonTintMode(@Nullable PorterDuff.Mode tintMode) {
+        setButtonTint(mButtonTint, tintMode);
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the button drawable
+     * @attr ref android.R.styleable#CompoundButton_buttonTintMode
+     * @see #setButtonTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getButtonTintMode() {
+        return mButtonTintMode;
+    }
+
+    private void applyButtonTint() {
+        if (mButtonDrawable != null && mHasButtonTint) {
+            mButtonDrawable = mButtonDrawable.mutate();
+            mButtonDrawable.setTint(mButtonTint, mButtonTintMode);
+        }
     }
 
     @Override
@@ -320,10 +430,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    protected void setDrawableHotspot(float x, float y) {
-        super.setDrawableHotspot(x, y);
+    public void drawableHotspotChanged(float x, float y) {
+        super.drawableHotspotChanged(x, y);
 
         if (mButtonDrawable != null) {
             mButtonDrawable.setHotspot(x, y);
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index 2502954..90fec23 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -286,6 +286,22 @@
         mTargetDisplacement = 0.5f;
     }
 
+    /**
+     * Set the color of this edge effect in argb.
+     *
+     * @param color Color in argb
+     */
+    public void setColor(int color) {
+        mPaint.setColor(color);
+    }
+
+    /**
+     * Return the color of this edge effect in argb.
+     * @return The color of this edge effect in argb
+     */
+    public int getColor() {
+        return mPaint.getColor();
+    }
 
     /**
      * Draw into the provided canvas. Assumes that the canvas has been rotated
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 4467128..13a0849 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1420,7 +1420,7 @@
                     blockDisplayList.setLeftTopRightBottom(left, top, right, bottom);
                 }
 
-                ((HardwareCanvas) canvas).drawDisplayList(blockDisplayList, null,
+                ((HardwareCanvas) canvas).drawRenderNode(blockDisplayList, null,
                         0 /* no child clipping, our TextView parent enforces it */);
 
                 endOfPreviousBlock = blockEndLine;
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 01a6b8a..34f333e 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -18,9 +18,12 @@
 
 import java.util.ArrayList;
 
+import android.annotation.Nullable;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Canvas;
+import android.graphics.PorterDuff;
 import android.graphics.Rect;
 import android.graphics.Region;
 import android.graphics.drawable.Drawable;
@@ -33,6 +36,8 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.RemoteViews.RemoteView;
 
+import com.android.internal.R;
+
 
 /**
  * FrameLayout is designed to block out an area on the screen to display
@@ -62,6 +67,9 @@
 
     @ViewDebug.ExportedProperty(category = "drawing")
     private Drawable mForeground;
+    private ColorStateList mForegroundTint = null;
+    private PorterDuff.Mode mForegroundTintMode = PorterDuff.Mode.SRC_ATOP;
+    private boolean mHasForegroundTint = false;
 
     @ViewDebug.ExportedProperty(category = "padding")
     private int mForegroundPaddingLeft = 0;
@@ -119,6 +127,15 @@
             setMeasureAllChildren(true);
         }
 
+        if (a.hasValue(R.styleable.FrameLayout_foregroundTint)) {
+            mForegroundTint = a.getColorStateList(R.styleable.FrameLayout_foregroundTint);
+            mForegroundTintMode = Drawable.parseTintMode(a.getInt(
+                    R.styleable.FrameLayout_foregroundTintMode, -1), mForegroundTintMode);
+            mHasForegroundTint = true;
+
+            applyForegroundTint();
+        }
+
         mForegroundInPadding = a.getBoolean(
                 com.android.internal.R.styleable.FrameLayout_foregroundInsidePadding, true);
 
@@ -205,10 +222,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    protected void setDrawableHotspot(float x, float y) {
-        super.setDrawableHotspot(x, y);
+    public void drawableHotspotChanged(float x, float y) {
+        super.drawableHotspotChanged(x, y);
 
         if (mForeground != null) {
             mForeground.setHotspot(x, y);
@@ -231,32 +247,34 @@
      * into account by ensuring that the children are inset to be placed
      * inside of the padding area.
      * 
-     * @param drawable The Drawable to be drawn on top of the children.
+     * @param d The Drawable to be drawn on top of the children.
      * 
      * @attr ref android.R.styleable#FrameLayout_foreground
      */
-    public void setForeground(Drawable drawable) {
-        if (mForeground != drawable) {
+    public void setForeground(Drawable d) {
+        if (mForeground != d) {
             if (mForeground != null) {
                 mForeground.setCallback(null);
                 unscheduleDrawable(mForeground);
             }
 
-            mForeground = drawable;
+            mForeground = d;
             mForegroundPaddingLeft = 0;
             mForegroundPaddingTop = 0;
             mForegroundPaddingRight = 0;
             mForegroundPaddingBottom = 0;
 
-            if (drawable != null) {
+            if (d != null) {
                 setWillNotDraw(false);
-                drawable.setCallback(this);
-                if (drawable.isStateful()) {
-                    drawable.setState(getDrawableState());
+                d.setCallback(this);
+                d.setLayoutDirection(getLayoutDirection());
+                if (d.isStateful()) {
+                    d.setState(getDrawableState());
                 }
+                applyForegroundTint();
                 if (mForegroundGravity == Gravity.FILL) {
                     Rect padding = new Rect();
-                    if (drawable.getPadding(padding)) {
+                    if (d.getPadding(padding)) {
                         mForegroundPaddingLeft = padding.left;
                         mForegroundPaddingTop = padding.top;
                         mForegroundPaddingRight = padding.right;
@@ -281,6 +299,89 @@
         return mForeground;
     }
 
+    /**
+     * Applies a tint to the foreground drawable.
+     * <p>
+     * Subsequent calls to {@link #setForeground(Drawable)} will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#FrameLayout_foregroundTint
+     * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+     */
+    private void setForegroundTint(@Nullable ColorStateList tint,
+            @Nullable PorterDuff.Mode tintMode) {
+        mForegroundTint = tint;
+        mForegroundTintMode = tintMode;
+        mHasForegroundTint = true;
+
+        applyForegroundTint();
+    }
+
+    /**
+     * Applies a tint to the foreground drawable. Does not modify the current
+     * tint mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+     * <p>
+     * Subsequent calls to {@link #setForeground(Drawable)} will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#FrameLayout_foregroundTint
+     * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+     */
+    public void setForegroundTint(@Nullable ColorStateList tint) {
+        setForegroundTint(tint, mForegroundTintMode);
+    }
+
+    /**
+     * @return the tint applied to the foreground drawable
+     * @attr ref android.R.styleable#FrameLayout_foregroundTint
+     * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public ColorStateList getForegroundTint() {
+        return mForegroundTint;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setForegroundTint(ColorStateList)}} to the foreground drawable.
+     * The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
+     * @see #setForegroundTint(ColorStateList)
+     */
+    public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+        setForegroundTint(mForegroundTint, tintMode);
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the foreground
+     *         drawable
+     * @attr ref android.R.styleable#FrameLayout_foregroundTintMode
+     * @see #setForegroundTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getForegroundTintMode() {
+        return mForegroundTintMode;
+    }
+
+    private void applyForegroundTint() {
+        if (mForeground != null && mHasForegroundTint) {
+            mForeground = mForeground.mutate();
+            mForeground.setTint(mForegroundTint, mForegroundTintMode);
+        }
+    }
+
     int getPaddingLeftWithForeground() {
         return mForegroundInPadding ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
             mPaddingLeft + mForegroundPaddingLeft;
diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java
index 04b18c1..93810b3 100644
--- a/core/java/android/widget/GridView.java
+++ b/core/java/android/widget/GridView.java
@@ -1326,12 +1326,28 @@
             if (sel != null) {
                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);
             } else {
-                mSelectedTop = 0;
-                mSelectorRect.setEmpty();
+                final boolean inTouchMode = mTouchMode > TOUCH_MODE_DOWN
+                        && mTouchMode < TOUCH_MODE_SCROLL;
+                if (inTouchMode) {
+                    // If the user's finger is down, select the motion position.
+                    final View child = getChildAt(mMotionPosition - mFirstPosition);
+                    if (child != null) {
+                        positionSelector(mMotionPosition, child);
+                    }
+                } else if (mSelectedPosition != INVALID_POSITION) {
+                    // If we had previously positioned the selector somewhere,
+                    // put it back there. It might not match up with the data,
+                    // but it's transitioning out so it's not a big deal.
+                    final View child = getChildAt(mSelectorPosition - mFirstPosition);
+                    if (child != null) {
+                        positionSelector(mSelectorPosition, child);
+                    }
+                } else {
+                    // Otherwise, clear selection.
+                    mSelectedTop = 0;
+                    mSelectorRect.setEmpty();
+                }
             }
 
             // Attempt to restore accessibility focus, if necessary.
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index a40b85e..5d578ca 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -16,8 +16,10 @@
 
 package android.widget;
 
+import android.annotation.Nullable;
 import android.content.ContentResolver;
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
@@ -44,6 +46,8 @@
 import android.view.accessibility.AccessibilityNodeInfo;
 import android.widget.RemoteViews.RemoteView;
 
+import com.android.internal.R;
+
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -75,13 +79,18 @@
     private int mMaxHeight = Integer.MAX_VALUE;
 
     // these are applied to the drawable
-    private ColorFilter mColorFilter;
+    private ColorFilter mColorFilter = null;
+    private boolean mHasColorFilter = false;
     private Xfermode mXfermode;
     private int mAlpha = 255;
     private int mViewAlphaScale = 256;
     private boolean mColorMod = false;
 
     private Drawable mDrawable = null;
+    private ColorStateList mDrawableTint = null;
+    private PorterDuff.Mode mDrawableTintMode = PorterDuff.Mode.SRC_ATOP;
+    private boolean mHasDrawableTint = false;
+
     private int[] mState = null;
     private boolean mMergeState = false;
     private int mLevel = 0;
@@ -154,17 +163,21 @@
         setMaxHeight(a.getDimensionPixelSize(
                 com.android.internal.R.styleable.ImageView_maxHeight, Integer.MAX_VALUE));
         
-        int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1);
+        final int index = a.getInt(com.android.internal.R.styleable.ImageView_scaleType, -1);
         if (index >= 0) {
             setScaleType(sScaleTypeArray[index]);
         }
 
-        int tint = a.getInt(com.android.internal.R.styleable.ImageView_tint, 0);
-        if (tint != 0) {
-            setColorFilter(tint);
+        if (a.hasValue(R.styleable.ImageView_tint)) {
+            mDrawableTint = a.getColorStateList(R.styleable.ImageView_tint);
+            mDrawableTintMode = Drawable.parseTintMode(a.getInt(
+                    R.styleable.ImageView_tintMode, -1), mDrawableTintMode);
+            mHasDrawableTint = true;
+
+            applyDrawableTint();
         }
-        
-        int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255);
+
+        final int alpha = a.getInt(com.android.internal.R.styleable.ImageView_drawableAlpha, 255);
         if (alpha != 255) {
             setAlpha(alpha);
         }
@@ -435,6 +448,88 @@
     }
 
     /**
+     * Applies a tint to the image drawable.
+     * <p>
+     * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#ImageView_tint
+     * @attr ref android.R.styleable#ImageView_tintMode
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+     */
+    private void setTint(@Nullable ColorStateList tint,
+            @Nullable PorterDuff.Mode tintMode) {
+        mDrawableTint = tint;
+        mDrawableTintMode = tintMode;
+        mHasDrawableTint = true;
+
+        applyDrawableTint();
+    }
+
+    /**
+     * Applies a tint to the image drawable. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+     * <p>
+     * Subsequent calls to {@link #setImageDrawable(Drawable)} will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#ImageView_tint
+     * @see Drawable#setTint(ColorStateList, PorterDuff.Mode)
+     */
+    public void setTint(@Nullable ColorStateList tint) {
+        setTint(tint, mDrawableTintMode);
+    }
+
+    /**
+     * @return the tint applied to the image drawable
+     * @attr ref android.R.styleable#ImageView_tint
+     * @see #setTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public ColorStateList getTint() {
+        return mDrawableTint;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setTint(ColorStateList)}} to the image drawable. The default
+     * mode is {@link PorterDuff.Mode#SRC_ATOP}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#ImageView_tintMode
+     * @see #setTint(ColorStateList)
+     */
+    public void setTintMode(@Nullable PorterDuff.Mode tintMode) {
+        setTint(mDrawableTint, tintMode);
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the image drawable
+     * @attr ref android.R.styleable#ImageView_tintMode
+     * @see #setTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getTintMode() {
+        return mDrawableTintMode;
+    }
+
+    private void applyDrawableTint() {
+        if (mDrawable != null && mHasDrawableTint) {
+            mDrawable = mDrawable.mutate();
+            mDrawable.setTint(mDrawableTint, mDrawableTintMode);
+        }
+    }
+
+    /**
      * Sets a Bitmap as the content of this ImageView.
      * 
      * @param bm The bitmap to set
@@ -709,17 +804,20 @@
             mDrawable.setCallback(null);
             unscheduleDrawable(mDrawable);
         }
+
         mDrawable = d;
+
         if (d != null) {
             d.setCallback(this);
+            d.setLayoutDirection(getLayoutDirection());
             if (d.isStateful()) {
                 d.setState(getDrawableState());
             }
-            d.setLevel(mLevel);
-            d.setLayoutDirection(getLayoutDirection());
             d.setVisible(getVisibility() == VISIBLE, true);
+            d.setLevel(mLevel);
             mDrawableWidth = d.getIntrinsicWidth();
             mDrawableHeight = d.getIntrinsicHeight();
+            applyDrawableTint();
             applyColorMod();
             configureBounds();
         } else {
@@ -1010,10 +1108,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    protected void setDrawableHotspot(float x, float y) {
-        super.setDrawableHotspot(x, y);
+    public void drawableHotspotChanged(float x, float y) {
+        super.drawableHotspotChanged(x, y);
 
         if (mDrawable != null) {
             mDrawable.setHotspot(x, y);
@@ -1177,6 +1274,7 @@
     public void setColorFilter(ColorFilter cf) {
         if (mColorFilter != cf) {
             mColorFilter = cf;
+            mHasColorFilter = true;
             mColorMod = true;
             applyColorMod();
             invalidate();
@@ -1231,7 +1329,9 @@
         // re-applied if the Drawable is changed.
         if (mDrawable != null && mColorMod) {
             mDrawable = mDrawable.mutate();
-            mDrawable.setColorFilter(mColorFilter);
+            if (mHasColorFilter) {
+                mDrawable.setColorFilter(mColorFilter);
+            }
             mDrawable.setXfermode(mXfermode);
             mDrawable.setAlpha(mAlpha * mViewAlphaScale >> 8);
         }
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index eeb8015..1baeca8 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -1718,14 +1718,24 @@
                 }
                 mSelectedTop = sel.getTop();
             } else {
-                // 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 boolean inTouchMode = mTouchMode == TOUCH_MODE_TAP
+                        || mTouchMode == TOUCH_MODE_DONE_WAITING;
+                if (inTouchMode) {
+                    // If the user's finger is down, select the motion position.
                     final View child = getChildAt(mMotionPosition - mFirstPosition);
-                    if (child != null)  {
+                    if (child != null) {
                         positionSelector(mMotionPosition, child);
                     }
+                } else if (mSelectorPosition != INVALID_POSITION) {
+                    // If we had previously positioned the selector somewhere,
+                    // put it back there. It might not match up with the data,
+                    // but it's transitioning out so it's not a big deal.
+                    final View child = getChildAt(mSelectorPosition - mFirstPosition);
+                    if (child != null) {
+                        positionSelector(mSelectorPosition, child);
+                    }
                 } else {
+                    // Otherwise, clear selection.
                     mSelectedTop = 0;
                     mSelectorRect.setEmpty();
                 }
diff --git a/core/java/android/widget/ProgressBar.java b/core/java/android/widget/ProgressBar.java
index af32f1c..394b255 100644
--- a/core/java/android/widget/ProgressBar.java
+++ b/core/java/android/widget/ProgressBar.java
@@ -16,13 +16,17 @@
 
 package android.widget;
 
+import android.annotation.Nullable;
+import android.graphics.PorterDuff;
 import com.android.internal.R;
 
 import android.content.Context;
+import android.content.res.ColorStateList;
 import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.BitmapShader;
 import android.graphics.Canvas;
+import android.graphics.PorterDuff.Mode;
 import android.graphics.Rect;
 import android.graphics.Shader;
 import android.graphics.drawable.Animatable;
@@ -210,8 +214,26 @@
     private Transformation mTransformation;
     private AlphaAnimation mAnimation;
     private boolean mHasAnimation;
+
     private Drawable mIndeterminateDrawable;
+    private ColorStateList mIndeterminateTint = null;
+    private PorterDuff.Mode mIndeterminateTintMode = PorterDuff.Mode.SRC_ATOP;
+    private boolean mHasIndeterminateTint = false;
+
     private Drawable mProgressDrawable;
+
+    private ColorStateList mProgressTint = null;
+    private PorterDuff.Mode mProgressTintMode = PorterDuff.Mode.SRC_ATOP;
+    private boolean mHasProgressTint = false;
+
+    private ColorStateList mProgressBackgroundTint = null;
+    private PorterDuff.Mode mProgressBackgroundTintMode = PorterDuff.Mode.SRC_ATOP;
+    private boolean mHasProgressBackgroundTint = false;
+
+    private ColorStateList mSecondaryProgressTint = null;
+    private PorterDuff.Mode mSecondaryProgressTintMode = PorterDuff.Mode.SRC_ATOP;
+    private boolean mHasSecondaryProgressTint = false;
+
     private Drawable mCurrentDrawable;
     Bitmap mSampleTile;
     private boolean mNoInvalidate;
@@ -257,11 +279,11 @@
         
         mNoInvalidate = true;
         
-        Drawable drawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
-        if (drawable != null) {
+        final Drawable progressDrawable = a.getDrawable(R.styleable.ProgressBar_progressDrawable);
+        if (progressDrawable != null) {
             // Calling this method can set mMaxHeight, make sure the corresponding
             // XML attribute for mMaxHeight is read after calling this method
-            setProgressDrawableTiled(drawable);
+            setProgressDrawableTiled(progressDrawable);
         }
 
 
@@ -288,9 +310,10 @@
         setSecondaryProgress(
                 a.getInt(R.styleable.ProgressBar_secondaryProgress, mSecondaryProgress));
 
-        drawable = a.getDrawable(R.styleable.ProgressBar_indeterminateDrawable);
-        if (drawable != null) {
-            setIndeterminateDrawableTiled(drawable);
+        final Drawable indeterminateDrawable = a.getDrawable(
+                R.styleable.ProgressBar_indeterminateDrawable);
+        if (indeterminateDrawable != null) {
+            setIndeterminateDrawableTiled(indeterminateDrawable);
         }
 
         mOnlyIndeterminate = a.getBoolean(
@@ -303,6 +326,53 @@
 
         mMirrorForRtl = a.getBoolean(R.styleable.ProgressBar_mirrorForRtl, mMirrorForRtl);
 
+        if (a.hasValue(R.styleable.ProgressBar_progressTint)) {
+            mProgressTint = a.getColorStateList(
+                    R.styleable.ProgressBar_progressTint);
+            mProgressTintMode = Drawable.parseTintMode(a.getInt(
+                    R.styleable.ProgressBar_progressBackgroundTintMode, -1),
+                    mProgressTintMode);
+            mHasProgressTint = true;
+
+            applyProgressLayerTint(R.id.progress, mProgressTint,
+                    mProgressTintMode, true);
+        }
+
+        if (a.hasValue(R.styleable.ProgressBar_progressBackgroundTint)) {
+            mProgressBackgroundTint = a.getColorStateList(
+                    R.styleable.ProgressBar_progressBackgroundTint);
+            mProgressBackgroundTintMode = Drawable.parseTintMode(a.getInt(
+                    R.styleable.ProgressBar_progressTintMode, -1),
+                    mProgressBackgroundTintMode);
+            mHasProgressBackgroundTint = true;
+
+            applyProgressLayerTint(R.id.background, mProgressBackgroundTint,
+                    mProgressBackgroundTintMode, false);
+        }
+
+        if (a.hasValue(R.styleable.ProgressBar_secondaryProgressTint)) {
+            mSecondaryProgressTint = a.getColorStateList(
+                    R.styleable.ProgressBar_secondaryProgressTint);
+            mSecondaryProgressTintMode = Drawable.parseTintMode(a.getInt(
+                    R.styleable.ProgressBar_secondaryProgressTintMode, -1),
+                    mSecondaryProgressTintMode);
+            mHasSecondaryProgressTint = true;
+
+            applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint,
+                    mSecondaryProgressTintMode, false);
+        }
+
+        if (a.hasValue(R.styleable.ProgressBar_indeterminateTint)) {
+            mIndeterminateTint = a.getColorStateList(
+                    R.styleable.ProgressBar_indeterminateTint);
+            mIndeterminateTintMode = Drawable.parseTintMode(a.getInt(
+                    R.styleable.ProgressBar_indeterminateTintMode, -1),
+                    mIndeterminateTintMode);
+            mHasIndeterminateTint = true;
+
+            applyIndeterminateTint();
+        }
+
         a.recycle();
 
         // If not explicitly specified this view is important for accessibility.
@@ -479,16 +549,111 @@
      * @see #setIndeterminate(boolean)
      */
     public void setIndeterminateDrawable(Drawable d) {
-        if (d != null) {
-            d.setCallback(this);
+        if (mIndeterminateDrawable != d) {
+            if (mIndeterminateDrawable != null) {
+                mIndeterminateDrawable.setCallback(null);
+                unscheduleDrawable(mIndeterminateDrawable);
+            }
+
+            mIndeterminateDrawable = d;
+
+            if (d != null) {
+                d.setCallback(this);
+                d.setLayoutDirection(getLayoutDirection());
+                if (d.isStateful()) {
+                    d.setState(getDrawableState());
+                }
+                applyIndeterminateTint();
+            }
+
+            if (mIndeterminate) {
+                mCurrentDrawable = d;
+                postInvalidate();
+            }
         }
-        mIndeterminateDrawable = d;
-        if (mIndeterminateDrawable != null && canResolveLayoutDirection()) {
-            mIndeterminateDrawable.setLayoutDirection(getLayoutDirection());
-        }
-        if (mIndeterminate) {
-            mCurrentDrawable = d;
-            postInvalidate();
+    }
+
+    /**
+     * Applies a tint to the indeterminate drawable.
+     * <p>
+     * Subsequent calls to {@link #setVisibilminateDrawable(Drawable)} will
+     * automatically mutate the drawable and apply the specified tint and
+     * tint mode using
+     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#ProgressBar_indeterminateTint
+     * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
+     * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+     */
+    private void setIndeterminateTint(@Nullable ColorStateList tint,
+            @Nullable PorterDuff.Mode tintMode) {
+        mIndeterminateTint = tint;
+        mIndeterminateTintMode = tintMode;
+        mHasIndeterminateTint = true;
+
+        applyIndeterminateTint();
+    }
+
+    /**
+     * Applies a tint to the indeterminate drawable. Does not modify the
+     * current tint mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+     * <p>
+     * Subsequent calls to {@link #setIndeterminateDrawable(Drawable)} will
+     * automatically mutate the drawable and apply the specified tint and
+     * tint mode using
+     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#ProgressBar_indeterminateTint
+     * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+     */
+    public void setIndeterminateTint(@Nullable ColorStateList tint) {
+        setIndeterminateTint(tint, mIndeterminateTintMode);
+    }
+
+    /**
+     * @return the tint applied to the indeterminate drawable
+     * @attr ref android.R.styleable#ProgressBar_indeterminateTint
+     * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public ColorStateList getIndeterminateTint() {
+        return mIndeterminateTint;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setIndeterminateTint(ColorStateList)} to the indeterminate
+     * drawable. The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
+     * @see #setIndeterminateTint(ColorStateList)
+     */
+    public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) {
+        setIndeterminateTint(mIndeterminateTint, tintMode);
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the indeterminate drawable
+     * @attr ref android.R.styleable#ProgressBar_indeterminateTintMode
+     * @see #setIndeterminateTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getIndeterminateTintMode() {
+        return mIndeterminateTintMode;
+    }
+
+    private void applyIndeterminateTint() {
+        if (mIndeterminateDrawable != null && mHasIndeterminateTint) {
+            mIndeterminateDrawable = mIndeterminateDrawable.mutate();
+            mIndeterminateDrawable.setTint(mIndeterminateTint, mIndeterminateTintMode);
         }
     }
 
@@ -532,42 +697,340 @@
      * @see #setIndeterminate(boolean)
      */
     public void setProgressDrawable(Drawable d) {
-        boolean needUpdate;
-        if (mProgressDrawable != null && d != mProgressDrawable) {
-            mProgressDrawable.setCallback(null);
-            needUpdate = true;
-        } else {
-            needUpdate = false;
-        }
+        if (mProgressDrawable != d) {
+            if (mProgressDrawable != null) {
+                mProgressDrawable.setCallback(null);
+                unscheduleDrawable(mProgressDrawable);
+            }
 
-        if (d != null) {
-            d.setCallback(this);
-            if (canResolveLayoutDirection()) {
+            mProgressDrawable = d;
+
+            if (d != null) {
+                d.setCallback(this);
                 d.setLayoutDirection(getLayoutDirection());
+                if (d.isStateful()) {
+                    d.setState(getDrawableState());
+                }
+
+                // Make sure the ProgressBar is always tall enough
+                int drawableHeight = d.getMinimumHeight();
+                if (mMaxHeight < drawableHeight) {
+                    mMaxHeight = drawableHeight;
+                    requestLayout();
+                }
+
+                if (mHasProgressTint) {
+                    applyProgressLayerTint(R.id.progress, mProgressTint, mProgressTintMode, true);
+                }
+
+                if (mHasProgressBackgroundTint) {
+                    applyProgressLayerTint(R.id.background, mProgressBackgroundTint,
+                            mProgressBackgroundTintMode, false);
+                }
+
+                if (mHasSecondaryProgressTint) {
+                    applyProgressLayerTint(R.id.secondaryProgress, mSecondaryProgressTint,
+                            mSecondaryProgressTintMode, false);
+                }
             }
 
-            // Make sure the ProgressBar is always tall enough
-            int drawableHeight = d.getMinimumHeight();
-            if (mMaxHeight < drawableHeight) {
-                mMaxHeight = drawableHeight;
-                requestLayout();
+            if (!mIndeterminate) {
+                mCurrentDrawable = d;
+                postInvalidate();
             }
-        }
-        mProgressDrawable = d;
-        if (!mIndeterminate) {
-            mCurrentDrawable = d;
-            postInvalidate();
-        }
 
-        if (needUpdate) {
             updateDrawableBounds(getWidth(), getHeight());
             updateDrawableState();
+
             doRefreshProgress(R.id.progress, mProgress, false, false);
             doRefreshProgress(R.id.secondaryProgress, mSecondaryProgress, false, false);
         }
     }
 
     /**
+     * Applies a tint to the progress indicator, if one exists, or to the
+     * entire progress drawable otherwise.
+     * <p>
+     * The progress indicator should be specified as a layer with
+     * id {@link android.R.id#progress} in a {@link LayerDrawable}
+     * used as the progress drawable.
+     * <p>
+     * Subsequent calls to {@link #setProgressDrawable(Drawable)} will
+     * automatically mutate the drawable and apply the specified tint and
+     * tint mode using
+     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#ProgressBar_progressTint
+     * @attr ref android.R.styleable#ProgressBar_progressTintMode
+     * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+     */
+    private void setProgressTint(@Nullable ColorStateList tint,
+            @Nullable PorterDuff.Mode tintMode) {
+        mProgressTint = tint;
+        mProgressTintMode = tintMode;
+        mHasProgressTint = true;
+
+        applyProgressLayerTint(R.id.progress, tint, tintMode, true);
+    }
+
+    /**
+     * Applies a tint to the progress indicator, if one exists, or to the
+     * entire progress drawable otherwise. Does not modify the current tint
+     * mode, which is {@link PorterDuff.Mode#SRC_ATOP} by default.
+     * <p>
+     * The progress indicator should be specified as a layer with
+     * id {@link android.R.id#progress} in a {@link LayerDrawable}
+     * used as the progress drawable.
+     * <p>
+     * Subsequent calls to {@link #setProgressDrawable(Drawable)} will
+     * automatically mutate the drawable and apply the specified tint and
+     * tint mode using
+     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#ProgressBar_progressTint
+     * @see #setProgressTint(ColorStateList)
+     */
+    public void setProgressTint(@Nullable ColorStateList tint) {
+        setProgressTint(tint, mProgressTintMode);
+    }
+
+    /**
+     * @return the tint applied to the progress drawable
+     * @attr ref android.R.styleable#ProgressBar_progressTint
+     * @see #setProgressTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public ColorStateList getProgressTint() {
+        return mProgressTint;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setProgressTint(ColorStateList)}} to the progress
+     * indicator. The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#ProgressBar_progressTintMode
+     * @see #setProgressTint(ColorStateList)
+     */
+    public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
+        setProgressTint(mProgressTint, tintMode);
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the progress drawable
+     * @attr ref android.R.styleable#ProgressBar_progressTintMode
+     * @see #setProgressTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getProgressTintMode() {
+        return mProgressTintMode;
+    }
+
+    /**
+     * Applies a tint to the progress background, if one exists.
+     * <p>
+     * The progress background must be specified as a layer with
+     * id {@link android.R.id#background} in a {@link LayerDrawable}
+     * used as the progress drawable.
+     * <p>
+     * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+     * drawable contains a progress background will automatically mutate the
+     * drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
+     * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
+     * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+     */
+    private void setProgressBackgroundTint(@Nullable ColorStateList tint,
+            @Nullable PorterDuff.Mode tintMode) {
+        mProgressBackgroundTint = tint;
+        mProgressBackgroundTintMode = tintMode;
+        mHasProgressBackgroundTint = true;
+
+        applyProgressLayerTint(R.id.background, tint, tintMode, false);
+    }
+
+    /**
+     * Applies a tint to the progress background, if one exists. Does not
+     * modify the current tint mode, which is
+     * {@link PorterDuff.Mode#SRC_ATOP} by default.
+     * <p>
+     * The progress background must be specified as a layer with
+     * id {@link android.R.id#background} in a {@link LayerDrawable}
+     * used as the progress drawable.
+     * <p>
+     * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+     * drawable contains a progress background will automatically mutate the
+     * drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
+     * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+     */
+    public void setProgressBackgroundTint(@Nullable ColorStateList tint) {
+        setProgressBackgroundTint(tint, mProgressBackgroundTintMode);
+    }
+
+    /**
+     * @return the tint applied to the progress background
+     * @attr ref android.R.styleable#ProgressBar_progressBackgroundTint
+     * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public ColorStateList getProgressBackgroundTint() {
+        return mProgressBackgroundTint;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setProgressBackgroundTint(ColorStateList)}} to the progress
+     * background. The default mode is {@link PorterDuff.Mode#SRC_ATOP}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
+     * @see #setProgressBackgroundTint(ColorStateList)
+     */
+    public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) {
+        setProgressBackgroundTint(mProgressBackgroundTint, tintMode);
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the progress
+     *         background
+     * @attr ref android.R.styleable#ProgressBar_progressBackgroundTintMode
+     * @see #setProgressBackgroundTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getProgressBackgroundTintMode() {
+        return mProgressBackgroundTintMode;
+    }
+
+    /**
+     * Applies a tint to the secondary progress indicator, if one exists.
+     * <p>
+     * The secondary progress indicator must be specified as a layer with
+     * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable}
+     * used as the progress drawable.
+     * <p>
+     * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+     * drawable contains a secondary progress indicator will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
+     * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
+     * @see Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)
+     */
+    private void setSecondaryProgressTint(@Nullable ColorStateList tint,
+            @Nullable PorterDuff.Mode tintMode) {
+        mSecondaryProgressTint = tint;
+        mSecondaryProgressTintMode = tintMode;
+        mHasSecondaryProgressTint = true;
+
+        applyProgressLayerTint(R.id.secondaryProgress, tint, tintMode, false);
+    }
+
+    /**
+     * Applies a tint to the secondary progress indicator, if one exists.
+     * Does not modify the current tint mode, which is
+     * {@link PorterDuff.Mode#SRC_ATOP} by default.
+     * <p>
+     * The secondary progress indicator must be specified as a layer with
+     * id {@link android.R.id#secondaryProgress} in a {@link LayerDrawable}
+     * used as the progress drawable.
+     * <p>
+     * Subsequent calls to {@link #setProgressDrawable(Drawable)} where the
+     * drawable contains a secondary progress indicator will automatically
+     * mutate the drawable and apply the specified tint and tint mode using
+     * {@link Drawable#setTint(ColorStateList, android.graphics.PorterDuff.Mode)}.
+     *
+     * @param tint the tint to apply, may be {@code null} to clear tint
+     *
+     * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
+     * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+     */
+    public void setSecondaryProgressTint(@Nullable ColorStateList tint) {
+        setSecondaryProgressTint(tint, mSecondaryProgressTintMode);
+    }
+
+    /**
+     * @return the tint applied to the secondary progress drawable
+     * @attr ref android.R.styleable#ProgressBar_secondaryProgressTint
+     * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public ColorStateList getSecondaryProgressTint() {
+        return mSecondaryProgressTint;
+    }
+
+    /**
+     * Specifies the blending mode used to apply the tint specified by
+     * {@link #setSecondaryProgressTint(ColorStateList)}} to the secondary
+     * progress indicator. The default mode is
+     * {@link PorterDuff.Mode#SRC_ATOP}.
+     *
+     * @param tintMode the blending mode used to apply the tint, may be
+     *                 {@code null} to clear tint
+     * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
+     * @see #setSecondaryProgressTint(ColorStateList)
+     */
+    public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) {
+        setSecondaryProgressTint(mSecondaryProgressTint, tintMode);
+    }
+
+    /**
+     * @return the blending mode used to apply the tint to the secondary
+     *         progress drawable
+     * @attr ref android.R.styleable#ProgressBar_secondaryProgressTintMode
+     * @see #setSecondaryProgressTint(ColorStateList, PorterDuff.Mode)
+     */
+    @Nullable
+    public PorterDuff.Mode getSecondaryProgressTintMode() {
+        return mSecondaryProgressTintMode;
+    }
+
+    private void applyProgressLayerTint(int layerId, @Nullable ColorStateList tint,
+            @Nullable PorterDuff.Mode tintMode, boolean shouldFallback) {
+        final Drawable d = mProgressDrawable;
+        if (d != null) {
+            mProgressDrawable = d.mutate();
+
+            Drawable layer = null;
+            if (d instanceof LayerDrawable) {
+                layer = ((LayerDrawable) d).findDrawableByLayerId(layerId);
+            }
+
+            if (shouldFallback && layer == null) {
+                layer = d;
+            }
+
+            if (layer != null) {
+                layer.setTint(tint, tintMode);
+            }
+        }
+    }
+
+    /**
      * Define the tileable drawable used to draw the progress bar in
      * progress mode.
      * <p>
@@ -670,6 +1133,22 @@
         }
     }
 
+    private void setDrawableTint(int id, ColorStateList tint, Mode tintMode, boolean fallback) {
+        Drawable layer = null;
+
+        // We expect a layer drawable, so try to find the target ID.
+        final Drawable d = mCurrentDrawable;
+        if (d instanceof LayerDrawable) {
+            layer = ((LayerDrawable) d).findDrawableByLayerId(id);
+        }
+
+        if (fallback && layer == null) {
+            layer = d;
+        }
+
+        layer.mutate().setTint(tint, tintMode);
+    }
+
     private synchronized void doRefreshProgress(int id, int progress, boolean fromUser,
             boolean callBackToApp) {
         float scale = mMax > 0 ? (float) progress / (float) mMax : 0;
@@ -1144,10 +1623,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    protected void setDrawableHotspot(float x, float y) {
-        super.setDrawableHotspot(x, y);
+    public void drawableHotspotChanged(float x, float y) {
+        super.drawableHotspotChanged(x, y);
 
         if (mProgressDrawable != null) {
             mProgressDrawable.setHotspot(x, y);
diff --git a/core/java/android/widget/QuickContactBadge.java b/core/java/android/widget/QuickContactBadge.java
index 14d782d..23fa402 100644
--- a/core/java/android/widget/QuickContactBadge.java
+++ b/core/java/android/widget/QuickContactBadge.java
@@ -112,10 +112,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    protected void setDrawableHotspot(float x, float y) {
-        super.setDrawableHotspot(x, y);
+    public void drawableHotspotChanged(float x, float y) {
+        super.drawableHotspotChanged(x, y);
 
         if (mOverlay != null) {
             mOverlay.setHotspot(x, y);
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index f7d20b53..82637a1 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -1781,7 +1781,9 @@
         Parcel p = Parcel.obtain();
         writeToParcel(p, 0);
         p.setDataPosition(0);
-        return new RemoteViews(p);
+        RemoteViews rv = new RemoteViews(p);
+        p.recycle();
+        return rv;
     }
 
     public String getPackage() {
diff --git a/core/java/android/widget/Spinner.java b/core/java/android/widget/Spinner.java
index 9601d4a..9914800 100644
--- a/core/java/android/widget/Spinner.java
+++ b/core/java/android/widget/Spinner.java
@@ -25,6 +25,7 @@
 import android.database.DataSetObserver;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
+import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.AttributeSet;
@@ -427,9 +428,15 @@
      * {@link Adapter#getItemViewType(int) getItemViewType(int)} on the object
      * returned from {@link #getAdapter()} will always return 0. Calling
      * {@link Adapter#getViewTypeCount() getViewTypeCount()} will always return
-     * 1.
+     * 1. On API {@link Build.VERSION_CODES#L} and above, attempting to set an
+     * adapter with more than one view type will throw an
+     * {@link IllegalArgumentException}.
+     *
+     * @param adapter the adapter to set
      *
      * @see AbsSpinner#setAdapter(SpinnerAdapter)
+     * @throws IllegalArgumentException if the adapter has more than one view
+     *         type
      */
     @Override
     public void setAdapter(SpinnerAdapter adapter) {
@@ -437,6 +444,12 @@
 
         mRecycler.clear();
 
+        final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
+        if (targetSdkVersion >= Build.VERSION_CODES.L
+                && adapter != null && adapter.getViewTypeCount() != 1) {
+            throw new IllegalArgumentException("Spinner adapter view type count must be 1");
+        }
+
         if (mPopup != null) {
             mPopup.setAdapter(new DropDownAdapter(adapter));
         } else {
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index 03193a2..9a8380d 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -90,6 +90,7 @@
     private boolean mSplitTrack;
     private CharSequence mTextOn;
     private CharSequence mTextOff;
+    private boolean mShowText;
 
     private int mTouchMode;
     private int mTouchSlop;
@@ -188,6 +189,7 @@
         mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
         mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
         mTextOff = a.getText(com.android.internal.R.styleable.Switch_textOff);
+        mShowText = a.getBoolean(com.android.internal.R.styleable.Switch_showText, true);
         mThumbTextPadding = a.getDimensionPixelSize(
                 com.android.internal.R.styleable.Switch_thumbTextPadding, 0);
         mSwitchMinWidth = a.getDimensionPixelSize(
@@ -533,20 +535,43 @@
         requestLayout();
     }
 
+    /**
+     * Sets whether the on/off text should be displayed.
+     *
+     * @param showText {@code true} to display on/off text
+     * @hide
+     */
+    public void setShowText(boolean showText) {
+        if (mShowText != showText) {
+            mShowText = showText;
+            requestLayout();
+        }
+    }
+
+    /**
+     * @return whether the on/off text should be displayed
+     * @hide
+     */
+    public boolean getShowText() {
+        return mShowText;
+    }
+
     @Override
     public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
-        if (mOnLayout == null) {
-            mOnLayout = makeLayout(mTextOn);
-        }
+        if (mShowText) {
+            if (mOnLayout == null) {
+                mOnLayout = makeLayout(mTextOn);
+            }
 
-        if (mOffLayout == null) {
-            mOffLayout = makeLayout(mTextOff);
+            if (mOffLayout == null) {
+                mOffLayout = makeLayout(mTextOff);
+            }
         }
 
         mTrackDrawable.getPadding(mTempRect);
 
-        final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
-                + mThumbTextPadding * 2;
+        final int maxTextWidth = mShowText ? Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
+                + mThumbTextPadding * 2 : 0;
         mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth());
 
         final int switchWidth = Math.max(mSwitchMinWidth,
@@ -568,9 +593,10 @@
     @Override
     public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
         super.onPopulateAccessibilityEvent(event);
-        Layout layout =  isChecked() ? mOnLayout : mOffLayout;
-        if (layout != null && !TextUtils.isEmpty(layout.getText())) {
-            event.getText().add(layout.getText());
+
+        final CharSequence text = isChecked() ? mTextOn : mTextOff;
+        if (text != null) {
+            event.getText().add(text);
         }
     }
 
@@ -962,10 +988,9 @@
         invalidate();
     }
 
-    /** @hide */
     @Override
-    protected void setDrawableHotspot(float x, float y) {
-        super.setDrawableHotspot(x, y);
+    public void drawableHotspotChanged(float x, float y) {
+        super.drawableHotspotChanged(x, y);
 
         if (mThumbDrawable != null) {
             mThumbDrawable.setHotspot(x, y);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0f51e8b..d470586 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -3503,10 +3503,9 @@
         }
     }
 
-    /** @hide */
     @Override
-    protected void setDrawableHotspot(float x, float y) {
-        super.setDrawableHotspot(x, y);
+    public void drawableHotspotChanged(float x, float y) {
+        super.drawableHotspotChanged(x, y);
 
         final Drawables dr = mDrawables;
         if (dr != null) {
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 4f1cd68..00e0d17 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -124,6 +124,9 @@
     private CharSequence mTitleText;
     private CharSequence mSubtitleText;
 
+    private int mTitleTextColor;
+    private int mSubtitleTextColor;
+
     // Clear me after use.
     private final ArrayList<View> mTempViews = new ArrayList<View>();
 
@@ -498,7 +501,12 @@
                 mTitleTextView = new TextView(context);
                 mTitleTextView.setSingleLine();
                 mTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
-                mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
+                if (mTitleTextAppearance != 0) {
+                    mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
+                }
+                if (mTitleTextColor != 0) {
+                    mTitleTextView.setTextColor(mTitleTextColor);
+                }
             }
             if (mTitleTextView.getParent() == null) {
                 addSystemView(mTitleTextView);
@@ -546,7 +554,12 @@
                 mSubtitleTextView = new TextView(context);
                 mSubtitleTextView.setSingleLine();
                 mSubtitleTextView.setEllipsize(TextUtils.TruncateAt.END);
-                mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance);
+                if (mSubtitleTextAppearance != 0) {
+                    mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance);
+                }
+                if (mSubtitleTextColor != 0) {
+                    mSubtitleTextView.setTextColor(mSubtitleTextColor);
+                }
             }
             if (mSubtitleTextView.getParent() == null) {
                 addSystemView(mSubtitleTextView);
@@ -583,6 +596,30 @@
     }
 
     /**
+     * Sets the text color of the title, if present.
+     *
+     * @param color The new text color in 0xAARRGGBB format
+     */
+    public void setTitleTextColor(int color) {
+        mTitleTextColor = color;
+        if (mTitleTextView != null) {
+            mTitleTextView.setTextColor(color);
+        }
+    }
+
+    /**
+     * Sets the text color of the subtitle, if present.
+     *
+     * @param color The new text color in 0xAARRGGBB format
+     */
+    public void setSubtitleTextColor(int color) {
+        mSubtitleTextColor = color;
+        if (mSubtitleTextView != null) {
+            mSubtitleTextView.setTextColor(color);
+        }
+    }
+
+    /**
      * Set the icon to use for the toolbar's navigation button.
      *
      * <p>The navigation button appears at the start of the toolbar if present. Setting an icon
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 0769b08..7a9137b 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -76,6 +76,8 @@
     void noteWifiRunningChanged(in WorkSource oldWs, in WorkSource newWs);
     void noteWifiStopped(in WorkSource ws);
     void noteWifiState(int wifiState, String accessPoint);
+    void noteWifiSupplicantStateChanged(int supplState, boolean failedAuth);
+    void noteWifiRssiChanged(int newRssi);
     void noteBluetoothOn();
     void noteBluetoothOff();
     void noteBluetoothState(int bluetoothState);
diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java
index 7e11850..4995ea1 100644
--- a/core/java/com/android/internal/app/ProcessStats.java
+++ b/core/java/com/android/internal/app/ProcessStats.java
@@ -28,7 +28,6 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TimeUtils;
-import android.webkit.WebViewFactory;
 
 import com.android.internal.util.GrowingArrayUtils;
 
@@ -55,6 +54,11 @@
     // that is done.
     public static long COMMIT_PERIOD = 3*60*60*1000;  // Commit current stats every 3 hours
 
+    // Minimum uptime period before committing.  If the COMMIT_PERIOD has elapsed but
+    // the total uptime has not exceeded this amount, then the commit will be held until
+    // it is reached.
+    public static long COMMIT_UPTIME_PERIOD = 60*60*1000;  // Must have at least 1 hour elapsed
+
     public static final int STATE_NOTHING = -1;
     public static final int STATE_PERSISTENT = 0;
     public static final int STATE_TOP = 1;
@@ -81,6 +85,24 @@
     public static final int PSS_USS_MAXIMUM = 6;
     public static final int PSS_COUNT = PSS_USS_MAXIMUM+1;
 
+    public static final int SYS_MEM_USAGE_SAMPLE_COUNT = 0;
+    public static final int SYS_MEM_USAGE_CACHED_MINIMUM = 1;
+    public static final int SYS_MEM_USAGE_CACHED_AVERAGE = 2;
+    public static final int SYS_MEM_USAGE_CACHED_MAXIMUM = 3;
+    public static final int SYS_MEM_USAGE_FREE_MINIMUM = 4;
+    public static final int SYS_MEM_USAGE_FREE_AVERAGE = 5;
+    public static final int SYS_MEM_USAGE_FREE_MAXIMUM = 6;
+    public static final int SYS_MEM_USAGE_ZRAM_MINIMUM = 7;
+    public static final int SYS_MEM_USAGE_ZRAM_AVERAGE = 8;
+    public static final int SYS_MEM_USAGE_ZRAM_MAXIMUM = 9;
+    public static final int SYS_MEM_USAGE_KERNEL_MINIMUM = 10;
+    public static final int SYS_MEM_USAGE_KERNEL_AVERAGE = 11;
+    public static final int SYS_MEM_USAGE_KERNEL_MAXIMUM = 12;
+    public static final int SYS_MEM_USAGE_NATIVE_MINIMUM = 13;
+    public static final int SYS_MEM_USAGE_NATIVE_AVERAGE = 14;
+    public static final int SYS_MEM_USAGE_NATIVE_MAXIMUM = 15;
+    public static final int SYS_MEM_USAGE_COUNT = SYS_MEM_USAGE_NATIVE_MAXIMUM+1;
+
     public static final int ADJ_NOTHING = -1;
     public static final int ADJ_MEM_FACTOR_NORMAL = 0;
     public static final int ADJ_MEM_FACTOR_MODERATE = 1;
@@ -174,7 +196,7 @@
     static final String CSV_SEP = "\t";
 
     // Current version of the parcel format.
-    private static final int PARCEL_VERSION = 14;
+    private static final int PARCEL_VERSION = 18;
     // In-memory Parcel magic number, used to detect attempts to unmarshall bad data
     private static final int MAGIC = 0x50535453;
 
@@ -200,11 +222,16 @@
     public int mMemFactor = STATE_NOTHING;
     public long mStartTime;
 
+    public int[] mSysMemUsageTable = null;
+    public int mSysMemUsageTableSize = 0;
+    public final long[] mSysMemUsageArgs = new long[SYS_MEM_USAGE_COUNT];
+
     public long mTimePeriodStartClock;
     public long mTimePeriodStartRealtime;
     public long mTimePeriodEndRealtime;
+    public long mTimePeriodStartUptime;
+    public long mTimePeriodEndUptime;
     String mRuntime;
-    String mWebView;
     boolean mRunning;
 
     static final int LONGS_SIZE = 4096;
@@ -304,11 +331,77 @@
             mMemFactorDurations[i] += other.mMemFactorDurations[i];
         }
 
+        for (int i=0; i<other.mSysMemUsageTableSize; i++) {
+            int ent = other.mSysMemUsageTable[i];
+            int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+            long[] longs = other.mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+            addSysMemUsage(state, longs, ((ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK));
+        }
+
         if (other.mTimePeriodStartClock < mTimePeriodStartClock) {
             mTimePeriodStartClock = other.mTimePeriodStartClock;
             mTimePeriodStartClockStr = other.mTimePeriodStartClockStr;
         }
         mTimePeriodEndRealtime += other.mTimePeriodEndRealtime - other.mTimePeriodStartRealtime;
+        mTimePeriodEndUptime += other.mTimePeriodEndUptime - other.mTimePeriodStartUptime;
+    }
+
+    public void addSysMemUsage(long cachedMem, long freeMem, long zramMem, long kernelMem,
+            long nativeMem) {
+        if (mMemFactor != STATE_NOTHING) {
+            int state = mMemFactor * STATE_COUNT;
+            mSysMemUsageArgs[SYS_MEM_USAGE_SAMPLE_COUNT] = 1;
+            for (int i=0; i<3; i++) {
+                mSysMemUsageArgs[SYS_MEM_USAGE_CACHED_MINIMUM + i] = cachedMem;
+                mSysMemUsageArgs[SYS_MEM_USAGE_FREE_MINIMUM + i] = freeMem;
+                mSysMemUsageArgs[SYS_MEM_USAGE_ZRAM_MINIMUM + i] = zramMem;
+                mSysMemUsageArgs[SYS_MEM_USAGE_KERNEL_MINIMUM + i] = kernelMem;
+                mSysMemUsageArgs[SYS_MEM_USAGE_NATIVE_MINIMUM + i] = nativeMem;
+            }
+            addSysMemUsage(state, mSysMemUsageArgs, 0);
+        }
+    }
+
+    void addSysMemUsage(int state, long[] data, int dataOff) {
+        int idx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, state);
+        int off;
+        if (idx >= 0) {
+            off = mSysMemUsageTable[idx];
+        } else {
+            mAddLongTable = mSysMemUsageTable;
+            mAddLongTableSize = mSysMemUsageTableSize;
+            off = addLongData(~idx, state, SYS_MEM_USAGE_COUNT);
+            mSysMemUsageTable = mAddLongTable;
+            mSysMemUsageTableSize = mAddLongTableSize;
+        }
+        long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+        idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK;
+        addSysMemUsage(longs, idx, data, dataOff);
+    }
+
+    static void addSysMemUsage(long[] dstData, int dstOff, long[] addData, int addOff) {
+        final long dstCount = dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT];
+        final long addCount = addData[addOff+SYS_MEM_USAGE_SAMPLE_COUNT];
+        if (dstCount == 0) {
+            dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = addCount;
+            for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i++) {
+                dstData[dstOff+i] = addData[addOff+i];
+            }
+        } else if (addCount > 0) {
+            dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = dstCount + addCount;
+            for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i+=3) {
+                if (dstData[dstOff+i] > addData[addOff+i]) {
+                    dstData[dstOff+i] = addData[addOff+i];
+                }
+                dstData[dstOff+i+1] = (long)(
+                        ((dstData[dstOff+i+1]*(double)dstCount)
+                                + (addData[addOff+i+1]*(double)addCount))
+                                / (dstCount+addCount) );
+                if (dstData[dstOff+i+2] < addData[addOff+i+2]) {
+                    dstData[dstOff+i+2] = addData[addOff+i+2];
+                }
+            }
+        }
     }
 
     public static final Parcelable.Creator<ProcessStats> CREATOR
@@ -564,6 +657,164 @@
         return totalTime;
     }
 
+    static class PssAggr {
+        long pss = 0;
+        long samples = 0;
+
+        void add(long newPss, long newSamples) {
+            pss = (long)( (pss*(double)samples) + (newPss*(double)newSamples) )
+                    / (samples+newSamples);
+            samples += newSamples;
+        }
+    }
+
+    public void computeTotalMemoryUse(TotalMemoryUseCollection data, long now) {
+        data.totalTime = 0;
+        for (int i=0; i<STATE_COUNT; i++) {
+            data.processStateWeight[i] = 0;
+            data.processStatePss[i] = 0;
+            data.processStateTime[i] = 0;
+            data.processStateSamples[i] = 0;
+        }
+        for (int i=0; i<SYS_MEM_USAGE_COUNT; i++) {
+            data.sysMemUsage[i] = 0;
+        }
+        data.sysMemCachedWeight = 0;
+        data.sysMemFreeWeight = 0;
+        data.sysMemZRamWeight = 0;
+        data.sysMemKernelWeight = 0;
+        data.sysMemNativeWeight = 0;
+        data.sysMemSamples = 0;
+        long[] totalMemUsage = new long[SYS_MEM_USAGE_COUNT];
+        for (int i=0; i<mSysMemUsageTableSize; i++) {
+            int ent = mSysMemUsageTable[i];
+            long[] longs = mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+            int idx = (ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK;
+            addSysMemUsage(totalMemUsage, 0, longs, idx);
+        }
+        for (int is=0; is<data.screenStates.length; is++) {
+            for (int im=0; im<data.memStates.length; im++) {
+                int memBucket = data.screenStates[is] + data.memStates[im];
+                int stateBucket = memBucket * STATE_COUNT;
+                long memTime = mMemFactorDurations[memBucket];
+                if (mMemFactor == memBucket) {
+                    memTime += now - mStartTime;
+                }
+                data.totalTime += memTime;
+                int sysIdx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, stateBucket);
+                long[] longs = totalMemUsage;
+                int idx = 0;
+                if (sysIdx >= 0) {
+                    int ent = mSysMemUsageTable[sysIdx];
+                    long[] tmpLongs = mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK);
+                    int tmpIdx = (ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK;
+                    if (tmpLongs[tmpIdx+SYS_MEM_USAGE_SAMPLE_COUNT] >= 3) {
+                        addSysMemUsage(data.sysMemUsage, 0, longs, idx);
+                        longs = tmpLongs;
+                        idx = tmpIdx;
+                    }
+                }
+                data.sysMemCachedWeight += longs[idx+SYS_MEM_USAGE_CACHED_AVERAGE]
+                        * (double)memTime;
+                data.sysMemFreeWeight += longs[idx+SYS_MEM_USAGE_FREE_AVERAGE]
+                        * (double)memTime;
+                data.sysMemZRamWeight += longs[idx+SYS_MEM_USAGE_ZRAM_AVERAGE]
+                        * (double)memTime;
+                data.sysMemKernelWeight += longs[idx+SYS_MEM_USAGE_KERNEL_AVERAGE]
+                        * (double)memTime;
+                data.sysMemNativeWeight += longs[idx+SYS_MEM_USAGE_NATIVE_AVERAGE]
+                        * (double)memTime;
+                data.sysMemSamples += longs[idx+SYS_MEM_USAGE_SAMPLE_COUNT];
+             }
+        }
+        ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap();
+        for (int iproc=0; iproc<procMap.size(); iproc++) {
+            SparseArray<ProcessState> uids = procMap.valueAt(iproc);
+            for (int iu=0; iu<uids.size(); iu++) {
+                final ProcessState proc = uids.valueAt(iu);
+                final PssAggr fgPss = new PssAggr();
+                final PssAggr bgPss = new PssAggr();
+                final PssAggr cachedPss = new PssAggr();
+                boolean havePss = false;
+                for (int i=0; i<proc.mDurationsTableSize; i++) {
+                    int off = proc.mDurationsTable[i];
+                    int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+                    int procState = type % STATE_COUNT;
+                    long samples = proc.getPssSampleCount(type);
+                    if (samples > 0) {
+                        long avg = proc.getPssAverage(type);
+                        havePss = true;
+                        if (procState <= STATE_IMPORTANT_FOREGROUND) {
+                            fgPss.add(avg, samples);
+                        } else if (procState <= STATE_RECEIVER) {
+                            bgPss.add(avg, samples);
+                        } else {
+                            cachedPss.add(avg, samples);
+                        }
+                    }
+                }
+                if (!havePss) {
+                    continue;
+                }
+                boolean fgHasBg = false;
+                boolean fgHasCached = false;
+                boolean bgHasCached = false;
+                if (fgPss.samples < 3 && bgPss.samples > 0) {
+                    fgHasBg = true;
+                    fgPss.add(bgPss.pss, bgPss.samples);
+                }
+                if (fgPss.samples < 3 && cachedPss.samples > 0) {
+                    fgHasCached = true;
+                    fgPss.add(cachedPss.pss, cachedPss.samples);
+                }
+                if (bgPss.samples < 3 && cachedPss.samples > 0) {
+                    bgHasCached = true;
+                    bgPss.add(cachedPss.pss, cachedPss.samples);
+                }
+                if (bgPss.samples < 3 && !fgHasBg && fgPss.samples > 0) {
+                    bgPss.add(fgPss.pss, fgPss.samples);
+                }
+                if (cachedPss.samples < 3 && !bgHasCached && bgPss.samples > 0) {
+                    cachedPss.add(bgPss.pss, bgPss.samples);
+                }
+                if (cachedPss.samples < 3 && !fgHasCached && fgPss.samples > 0) {
+                    cachedPss.add(fgPss.pss, fgPss.samples);
+                }
+                for (int i=0; i<proc.mDurationsTableSize; i++) {
+                    final int off = proc.mDurationsTable[i];
+                    final int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+                    long time = getLong(off, 0);
+                    if (proc.mCurState == type) {
+                        time += now - proc.mStartTime;
+                    }
+                    final int procState = type % STATE_COUNT;
+                    data.processStateTime[procState] += time;
+                    long samples = proc.getPssSampleCount(type);
+                    long avg;
+                    if (samples > 0) {
+                        avg = proc.getPssAverage(type);
+                    } else if (procState <= STATE_IMPORTANT_FOREGROUND) {
+                        samples = fgPss.samples;
+                        avg = fgPss.pss;
+                    } else if (procState <= STATE_RECEIVER) {
+                        samples = bgPss.samples;
+                        avg = bgPss.pss;
+                    } else {
+                        samples = cachedPss.samples;
+                        avg = cachedPss.pss;
+                    }
+                    double newAvg = ( (data.processStatePss[procState]
+                            * (double)data.processStateSamples[procState])
+                                + (avg*(double)samples)
+                            ) / (data.processStateSamples[procState]+samples);
+                    data.processStatePss[procState] = (long)newAvg;
+                    data.processStateSamples[procState] += samples;
+                    data.processStateWeight[procState] += avg * (double)time;
+                }
+            }
+        }
+    }
+
     static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc,
             int[] screenStates, int[] memStates, int[] procStates, long now) {
         long totalTime = 0;
@@ -679,6 +930,62 @@
         }
     }
 
+    long getSysMemUsageValue(int state, int index) {
+        int idx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, state);
+        return idx >= 0 ? getLong(mSysMemUsageTable[idx], index) : 0;
+    }
+
+    void dumpSysMemUsageCategory(PrintWriter pw, String prefix, String label,
+            int bucket, int index) {
+        pw.print(prefix); pw.print(label);
+        pw.print(": ");
+        printSizeValue(pw, getSysMemUsageValue(bucket, index) * 1024);
+        pw.print(" min, ");
+        printSizeValue(pw, getSysMemUsageValue(bucket, index + 1) * 1024);
+        pw.print(" avg, ");
+        printSizeValue(pw, getSysMemUsageValue(bucket, index+2) * 1024);
+        pw.println(" max");
+    }
+
+    void dumpSysMemUsage(PrintWriter pw, String prefix, int[] screenStates,
+            int[] memStates) {
+        int printedScreen = -1;
+        for (int is=0; is<screenStates.length; is++) {
+            int printedMem = -1;
+            for (int im=0; im<memStates.length; im++) {
+                final int iscreen = screenStates[is];
+                final int imem = memStates[im];
+                final int bucket = ((iscreen + imem) * STATE_COUNT);
+                long count = getSysMemUsageValue(bucket, SYS_MEM_USAGE_SAMPLE_COUNT);
+                if (count > 0) {
+                    pw.print(prefix);
+                    if (screenStates.length > 1) {
+                        printScreenLabel(pw, printedScreen != iscreen
+                                ? iscreen : STATE_NOTHING);
+                        printedScreen = iscreen;
+                    }
+                    if (memStates.length > 1) {
+                        printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, '\0');
+                        printedMem = imem;
+                    }
+                    pw.print(": ");
+                    pw.print(count);
+                    pw.println(" samples:");
+                    dumpSysMemUsageCategory(pw, prefix, "  Cached", bucket,
+                            SYS_MEM_USAGE_CACHED_MINIMUM);
+                    dumpSysMemUsageCategory(pw, prefix, "  Free", bucket,
+                            SYS_MEM_USAGE_FREE_MINIMUM);
+                    dumpSysMemUsageCategory(pw, prefix, "  ZRam", bucket,
+                            SYS_MEM_USAGE_ZRAM_MINIMUM);
+                    dumpSysMemUsageCategory(pw, prefix, "  Kernel", bucket,
+                            SYS_MEM_USAGE_KERNEL_MINIMUM);
+                    dumpSysMemUsageCategory(pw, prefix, "  Native", bucket,
+                            SYS_MEM_USAGE_NATIVE_MINIMUM);
+                }
+            }
+        }
+    }
+
     static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates,
             int[] memStates, int[] procStates) {
         final int NS = screenStates != null ? screenStates.length : 1;
@@ -1088,10 +1395,13 @@
         mTimePeriodStartClock = System.currentTimeMillis();
         buildTimePeriodStartClockStr();
         mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+        mTimePeriodStartUptime = mTimePeriodEndUptime = SystemClock.uptimeMillis();
         mLongs.clear();
         mLongs.add(new long[LONGS_SIZE]);
         mNextLong = 0;
         Arrays.fill(mMemFactorDurations, 0);
+        mSysMemUsageTable = null;
+        mSysMemUsageTableSize = 0;
         mStartTime = 0;
         mReadError = null;
         mFlags = 0;
@@ -1220,12 +1530,17 @@
 
     @Override
     public void writeToParcel(Parcel out, int flags) {
-        long now = SystemClock.uptimeMillis();
+        writeToParcel(out, SystemClock.uptimeMillis(), flags);
+    }
+
+    /** @hide */
+    public void writeToParcel(Parcel out, long now, int flags) {
         out.writeInt(MAGIC);
         out.writeInt(PARCEL_VERSION);
         out.writeInt(STATE_COUNT);
         out.writeInt(ADJ_COUNT);
         out.writeInt(PSS_COUNT);
+        out.writeInt(SYS_MEM_USAGE_COUNT);
         out.writeInt(LONGS_SIZE);
 
         mCommonStringToIndex = new ArrayMap<String, Integer>(mProcesses.mMap.size());
@@ -1268,8 +1583,9 @@
         out.writeLong(mTimePeriodStartClock);
         out.writeLong(mTimePeriodStartRealtime);
         out.writeLong(mTimePeriodEndRealtime);
+        out.writeLong(mTimePeriodStartUptime);
+        out.writeLong(mTimePeriodEndUptime);
         out.writeString(mRuntime);
-        out.writeString(mWebView);
         out.writeInt(mFlags);
 
         out.writeInt(mLongs.size());
@@ -1287,6 +1603,13 @@
         }
         writeCompactedLongArray(out, mMemFactorDurations, mMemFactorDurations.length);
 
+        out.writeInt(mSysMemUsageTableSize);
+        for (int i=0; i<mSysMemUsageTableSize; i++) {
+            if (DEBUG_PARCEL) Slog.i(TAG, "Writing sys mem usage #" + i + ": "
+                    + printLongOffset(mSysMemUsageTable[i]));
+            out.writeInt(mSysMemUsageTable[i]);
+        }
+
         out.writeInt(NPROC);
         for (int ip=0; ip<NPROC; ip++) {
             writeCommonString(out, procMap.keyAt(ip));
@@ -1417,6 +1740,9 @@
         if (!readCheckedInt(in, PSS_COUNT, "pss count")) {
             return;
         }
+        if (!readCheckedInt(in, SYS_MEM_USAGE_COUNT, "sys mem usage count")) {
+            return;
+        }
         if (!readCheckedInt(in, LONGS_SIZE, "longs size")) {
             return;
         }
@@ -1427,8 +1753,9 @@
         buildTimePeriodStartClockStr();
         mTimePeriodStartRealtime = in.readLong();
         mTimePeriodEndRealtime = in.readLong();
+        mTimePeriodStartUptime = in.readLong();
+        mTimePeriodEndUptime = in.readLong();
         mRuntime = in.readString();
-        mWebView = in.readString();
         mFlags = in.readInt();
 
         final int NLONGS = in.readInt();
@@ -1447,6 +1774,12 @@
 
         readCompactedLongArray(in, version, mMemFactorDurations, mMemFactorDurations.length);
 
+        mSysMemUsageTable = readTableFromParcel(in, TAG, "sys mem usage");
+        if (mSysMemUsageTable == BAD_TABLE) {
+            return;
+        }
+        mSysMemUsageTableSize = mSysMemUsageTable != null ? mSysMemUsageTable.length : 0;
+
         int NPROC = in.readInt();
         if (NPROC < 0) {
             mReadError = "bad process count: " + NPROC;
@@ -1826,6 +2159,10 @@
             boolean dumpAll, boolean activeOnly) {
         long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor,
                 mStartTime, now);
+        if (mSysMemUsageTable != null) {
+            pw.println("System memory usage:");
+            dumpSysMemUsage(pw, "  ", ALL_SCREEN_ADJ, ALL_MEM_ADJ);
+        }
         ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
         boolean printedHeader = false;
         boolean sepNeeded = false;
@@ -2089,10 +2426,57 @@
         dumpTotalsLocked(pw, now);
     }
 
+    long printMemoryCategory(PrintWriter pw, String prefix, String label, double memWeight,
+            long totalTime, long curTotalMem, int samples) {
+        if (memWeight != 0) {
+            long mem = (long)(memWeight * 1024 / totalTime);
+            pw.print(prefix);
+            pw.print(label);
+            pw.print(": ");
+            printSizeValue(pw, mem);
+            pw.print(" (");
+            pw.print(samples);
+            pw.print(" samples)");
+            pw.println();
+            return curTotalMem + mem;
+        }
+        return curTotalMem;
+    }
+
     void dumpTotalsLocked(PrintWriter pw, long now) {
         pw.println("Run time Stats:");
         dumpSingleTime(pw, "  ", mMemFactorDurations, mMemFactor, mStartTime, now);
         pw.println();
+        pw.println("Memory usage:");
+        TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ,
+                ALL_MEM_ADJ);
+        computeTotalMemoryUse(totalMem, now);
+        long totalPss = 0;
+        totalPss = printMemoryCategory(pw, "  ", "Kernel ", totalMem.sysMemKernelWeight,
+                totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+        totalPss = printMemoryCategory(pw, "  ", "Native ", totalMem.sysMemNativeWeight,
+                totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+        for (int i=0; i<STATE_COUNT; i++) {
+            // Skip restarting service state -- that is not actually a running process.
+            if (i != STATE_SERVICE_RESTARTING) {
+                totalPss = printMemoryCategory(pw, "  ", STATE_NAMES[i],
+                        totalMem.processStateWeight[i], totalMem.totalTime, totalPss,
+                        totalMem.processStateSamples[i]);
+            }
+        }
+        totalPss = printMemoryCategory(pw, "  ", "Cached ", totalMem.sysMemCachedWeight,
+                totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+        totalPss = printMemoryCategory(pw, "  ", "Free   ", totalMem.sysMemFreeWeight,
+                totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+        totalPss = printMemoryCategory(pw, "  ", "Z-Ram   ", totalMem.sysMemZRamWeight,
+                totalMem.totalTime, totalPss, totalMem.sysMemSamples);
+        pw.print("  TOTAL  : ");
+        printSizeValue(pw, totalPss);
+        pw.println();
+        printMemoryCategory(pw, "  ", STATE_NAMES[STATE_SERVICE_RESTARTING],
+                totalMem.processStateWeight[STATE_SERVICE_RESTARTING], totalMem.totalTime, totalPss,
+                totalMem.processStateSamples[STATE_SERVICE_RESTARTING]);
+        pw.println();
         pw.print("          Start time: ");
         pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock));
         pw.println();
@@ -2118,8 +2502,6 @@
         }
         pw.print(' ');
         pw.print(mRuntime);
-        pw.print(' ');
-        pw.print(mWebView);
         pw.println();
     }
 
@@ -2208,7 +2590,7 @@
     public void dumpCheckinLocked(PrintWriter pw, String reqPackage) {
         final long now = SystemClock.uptimeMillis();
         final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap();
-        pw.println("vers,4");
+        pw.println("vers,5");
         pw.print("period,"); pw.print(mTimePeriodStartClockStr);
         pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(",");
         pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime);
@@ -2229,7 +2611,7 @@
             pw.print(",partial");
         }
         pw.println();
-        pw.print("config,"); pw.print(mRuntime); pw.print(','); pw.println(mWebView);
+        pw.print("config,"); pw.println(mRuntime);
         for (int ip=0; ip<pkgMap.size(); ip++) {
             final String pkgName = pkgMap.keyAt(ip);
             if (reqPackage != null && !reqPackage.equals(pkgName)) {
@@ -2362,6 +2744,53 @@
         pw.print("total");
         dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor,
                 mStartTime, now);
+        if (mSysMemUsageTable != null) {
+            pw.print("sysmemusage");
+            for (int i=0; i<mSysMemUsageTableSize; i++) {
+                int off = mSysMemUsageTable[i];
+                int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK;
+                pw.print(",");
+                printProcStateTag(pw, type);
+                for (int j=SYS_MEM_USAGE_SAMPLE_COUNT; j<SYS_MEM_USAGE_COUNT; j++) {
+                    if (j > SYS_MEM_USAGE_CACHED_MINIMUM) {
+                        pw.print(":");
+                    }
+                    pw.print(getLong(off, j));
+                }
+            }
+        }
+        pw.println();
+        TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ,
+                ALL_MEM_ADJ);
+        computeTotalMemoryUse(totalMem, now);
+        pw.print("weights,");
+        pw.print(totalMem.totalTime);
+        pw.print(",");
+        pw.print(totalMem.sysMemCachedWeight);
+        pw.print(":");
+        pw.print(totalMem.sysMemSamples);
+        pw.print(",");
+        pw.print(totalMem.sysMemFreeWeight);
+        pw.print(":");
+        pw.print(totalMem.sysMemSamples);
+        pw.print(",");
+        pw.print(totalMem.sysMemZRamWeight);
+        pw.print(":");
+        pw.print(totalMem.sysMemSamples);
+        pw.print(",");
+        pw.print(totalMem.sysMemKernelWeight);
+        pw.print(":");
+        pw.print(totalMem.sysMemSamples);
+        pw.print(",");
+        pw.print(totalMem.sysMemNativeWeight);
+        pw.print(":");
+        pw.print(totalMem.sysMemSamples);
+        for (int i=0; i<STATE_COUNT; i++) {
+            pw.print(",");
+            pw.print(totalMem.processStateWeight[i]);
+            pw.print(":");
+            pw.print(totalMem.processStateSamples[i]);
+        }
         pw.println();
     }
 
@@ -2452,6 +2881,15 @@
         }
     }
 
+    final public static class ProcessStateHolder {
+        public final int appVersion;
+        public ProcessStats.ProcessState state;
+
+        public ProcessStateHolder(int _appVersion) {
+            appVersion = _appVersion;
+        }
+    }
+
     public static final class ProcessState extends DurationsTable {
         public ProcessState mCommonProcess;
         public final String mPackage;
@@ -2660,7 +3098,7 @@
          * @param pkgList Processes to update.
          */
         public void setState(int state, int memFactor, long now,
-                ArrayMap<String, ProcessState> pkgList) {
+                ArrayMap<String, ProcessStateHolder> pkgList) {
             if (state < 0) {
                 state = mNumStartedServices > 0
                         ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING;
@@ -2770,7 +3208,7 @@
         }
 
         public void addPss(long pss, long uss, boolean always,
-                ArrayMap<String, ProcessState> pkgList) {
+                ArrayMap<String, ProcessStateHolder> pkgList) {
             ensureNotDead();
             if (!always) {
                 if (mLastPssState == mCurState && SystemClock.uptimeMillis()
@@ -2845,7 +3283,7 @@
             }
         }
 
-        public void reportExcessiveWake(ArrayMap<String, ProcessState> pkgList) {
+        public void reportExcessiveWake(ArrayMap<String, ProcessStateHolder> pkgList) {
             ensureNotDead();
             mCommonProcess.mNumExcessiveWake++;
             if (!mCommonProcess.mMultiPackage) {
@@ -2857,7 +3295,7 @@
             }
         }
 
-        public void reportExcessiveCpu(ArrayMap<String, ProcessState> pkgList) {
+        public void reportExcessiveCpu(ArrayMap<String, ProcessStateHolder> pkgList) {
             ensureNotDead();
             mCommonProcess.mNumExcessiveCpu++;
             if (!mCommonProcess.mMultiPackage) {
@@ -2888,7 +3326,7 @@
             }
         }
 
-        public void reportCachedKill(ArrayMap<String, ProcessState> pkgList, long pss) {
+        public void reportCachedKill(ArrayMap<String, ProcessStateHolder> pkgList, long pss) {
             ensureNotDead();
             mCommonProcess.addCachedKill(1, pss, pss, pss);
             if (!mCommonProcess.mMultiPackage) {
@@ -2925,8 +3363,10 @@
             return this;
         }
 
-        private ProcessState pullFixedProc(ArrayMap<String, ProcessState> pkgList, int index) {
-            ProcessState proc = pkgList.valueAt(index);
+        private ProcessState pullFixedProc(ArrayMap<String, ProcessStateHolder> pkgList,
+                int index) {
+            ProcessStateHolder holder = pkgList.valueAt(index);
+            ProcessState proc = holder.state;
             if (mDead && proc.mCommonProcess != proc) {
                 // Somehow we are contining to use a process state that is dead, because
                 // it was not being told it was active during the last commit.  We can recover
@@ -2959,7 +3399,7 @@
                     throw new IllegalStateException("Didn't create per-package process "
                             + proc.mName + " in pkg " + pkg.mPackageName + "/" + pkg.mUid);
                 }
-                pkgList.setValueAt(index, proc);
+                holder.state = proc;
             }
             return proc;
         }
@@ -3351,4 +3791,27 @@
             }
         }
     }
+
+    public static class TotalMemoryUseCollection {
+        final int[] screenStates;
+        final int[] memStates;
+
+        public TotalMemoryUseCollection(int[] _screenStates, int[] _memStates) {
+            screenStates = _screenStates;
+            memStates = _memStates;
+        }
+
+        public long totalTime;
+        public long[] processStatePss = new long[STATE_COUNT];
+        public double[] processStateWeight = new double[STATE_COUNT];
+        public long[] processStateTime = new long[STATE_COUNT];
+        public int[] processStateSamples = new int[STATE_COUNT];
+        public long[] sysMemUsage = new long[SYS_MEM_USAGE_COUNT];
+        public double sysMemCachedWeight;
+        public double sysMemFreeWeight;
+        public double sysMemZRamWeight;
+        public double sysMemKernelWeight;
+        public double sysMemNativeWeight;
+        public int sysMemSamples;
+    }
 }
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 183dd05..9d42738 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -259,11 +259,11 @@
         super.onRestoreInstanceState(savedInstanceState);
         if (mAlwaysUseOption) {
             final int checkedPos = mListView.getCheckedItemPosition();
-            final boolean enabled = checkedPos != ListView.INVALID_POSITION;
+            final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
             mLastSelected = checkedPos;
-            mAlwaysButton.setEnabled(enabled);
-            mOnceButton.setEnabled(enabled);
-            if (enabled) {
+            setAlwaysButtonEnabled(hasValidSelection, checkedPos);
+            mOnceButton.setEnabled(hasValidSelection);
+            if (hasValidSelection) {
                 mListView.setSelection(checkedPos);
             }
         }
@@ -274,7 +274,7 @@
         final int checkedPos = mListView.getCheckedItemPosition();
         final boolean hasValidSelection = checkedPos != ListView.INVALID_POSITION;
         if (mAlwaysUseOption && (!hasValidSelection || mLastSelected != checkedPos)) {
-            mAlwaysButton.setEnabled(hasValidSelection);
+            setAlwaysButtonEnabled(hasValidSelection, checkedPos);
             mOnceButton.setEnabled(hasValidSelection);
             if (hasValidSelection) {
                 mListView.smoothScrollToPosition(checkedPos);
@@ -285,6 +285,17 @@
         }
     }
 
+    private void setAlwaysButtonEnabled(boolean hasValidSelection, int checkedPos) {
+        boolean enabled = false;
+        if (hasValidSelection) {
+            ResolveInfo ri = mAdapter.resolveInfoForPosition(checkedPos);
+            if (ri.targetUserId == UserHandle.USER_CURRENT) {
+                enabled = true;
+            }
+        }
+        mAlwaysButton.setEnabled(enabled);
+    }
+
     public void onButtonClick(View v) {
         final int id = v.getId();
         startSelected(mListView.getCheckedItemPosition(), id == R.id.button_always);
diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java
index 87a80ac..7bd316f 100644
--- a/core/java/com/android/internal/app/WindowDecorActionBar.java
+++ b/core/java/com/android/internal/app/WindowDecorActionBar.java
@@ -830,7 +830,9 @@
     }
 
     public boolean isShowing() {
-        return mNowShowing && getHideOffset() < getHeight();
+        final int height = getHeight();
+        // Take into account the case where the bar has a 0 height due to not being measured yet.
+        return mNowShowing && (height == 0 || getHideOffset() < height);
     }
 
     void animateToMode(boolean toActionMode) {
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 960fa49..643225d 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -16,6 +16,7 @@
 
 package com.android.internal.backup;
 
+import android.app.backup.RestoreDescription;
 import android.app.backup.RestoreSet;
 import android.content.Intent;
 import android.content.pm.PackageInfo;
@@ -168,12 +169,25 @@
     int startRestore(long token, in PackageInfo[] packages);
 
     /**
-     * Get the package name of the next application with data in the backup store.
-     * @return The name of one of the packages supplied to {@link #startRestore},
-     *   or "" (the empty string) if no more backup data is available,
-     *   or null if an error occurred (the restore should be aborted and rescheduled).
+     * Get the package name of the next application with data in the backup store, plus
+     * a description of the structure of the restored archive: either TYPE_KEY_VALUE for
+     * an original-API key/value dataset, or TYPE_FULL_STREAM for a tarball-type archive stream.
+     *
+     * <p>If the package name in the returned RestoreDescription object is the singleton
+     * {@link RestoreDescription#NO_MORE_PACKAGES}, it indicates that no further data is available
+     * in the current restore session: all packages described in startRestore() have been
+     * processed.
+     *
+     * <p>If this method returns {@code null}, it means that a transport-level error has
+     * occurred and the entire restore operation should be abandoned.
+     *
+     * @return A RestoreDescription object containing the name of one of the packages
+     *   supplied to {@link #startRestore} plus an indicator of the data type of that
+     *   restore data; or {@link RestoreDescription#NO_MORE_PACKAGES} to indicate that
+     *   no more packages can be restored in this session; or {@code null} to indicate
+     *   a transport-level error.
      */
-    String nextRestorePackage();
+    RestoreDescription nextRestorePackage();
 
     /**
      * Get the data for the application returned by {@link #nextRestorePackage}.
@@ -188,7 +202,58 @@
      */
     void finishRestore();
 
+    // full backup stuff
+
     long requestFullBackupTime();
     int performFullBackup(in PackageInfo targetPackage, in ParcelFileDescriptor socket);
     int sendBackupData(int numBytes);
+
+    // full restore stuff
+
+    /**
+     * Ask the transport to provide data for the "current" package being restored.  This
+     * is the package that was just reported by {@link #nextRestorePackage()} as having
+     * {@link RestoreDescription#TYPE_FULL_STREAM} data.
+     *
+     * The transport writes some data to the socket supplied to this call, and returns
+     * the number of bytes written.  The system will then read that many bytes and
+     * stream them to the application's agent for restore, then will call this method again
+     * to receive the next chunk of the archive.  This sequence will be repeated until the
+     * transport returns zero indicating that all of the package's data has been delivered
+     * (or returns a negative value indicating some sort of hard error condition at the
+     * transport level).
+     *
+     * <p>After this method returns zero, the system will then call
+     * {@link #getNextFullRestorePackage()} to begin the restore process for the next
+     * application, and the sequence begins again.
+     *
+     * <p>The transport should always close this socket when returning from this method.
+     * Do not cache this socket across multiple calls or you may leak file descriptors.
+     *
+     * @param socket The file descriptor that the transport will use for delivering the
+     *    streamed archive.  The transport must close this socket in all cases when returning
+     *    from this method.
+     * @return 0 when no more data for the current package is available.  A positive value
+     *    indicates the presence of that many bytes to be delivered to the app.  Any negative
+     *    return value is treated as equivalent to {@link BackupTransport#TRANSPORT_ERROR},
+     *    indicating a fatal error condition that precludes further restore operations
+     *    on the current dataset.
+     */
+    int getNextFullRestoreDataChunk(in ParcelFileDescriptor socket);
+
+    /**
+     * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
+     * data for restore, it will invoke this method to tell the transport that it should
+     * abandon the data download for the current package.  The OS will then either call
+     * {@link #nextRestorePackage()} again to move on to restoring the next package in the
+     * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
+     * operation.
+     *
+     * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
+     *    current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
+     *    transport-level failure.  If the transport reports an error here, the entire restore
+     *    operation will immediately be finished with no further attempts to restore app data.
+     */
+    int abortFullRestore();
+
 }
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index c9d621d..b098de8 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -19,6 +19,7 @@
 import android.app.backup.BackupDataInput;
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupTransport;
+import android.app.backup.RestoreDescription;
 import android.app.backup.RestoreSet;
 import android.content.ComponentName;
 import android.content.Context;
@@ -63,18 +64,24 @@
     private static final String TRANSPORT_DESTINATION_STRING
             = "Backing up to debug-only private cache";
 
+    private static final String INCREMENTAL_DIR = "_delta";
+    private static final String FULL_DATA_DIR = "_full";
+
     // The currently-active restore set always has the same (nonzero!) token
     private static final long CURRENT_SET_TOKEN = 1;
 
     private Context mContext;
     private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup");
     private File mCurrentSetDir = new File(mDataDir, Long.toString(CURRENT_SET_TOKEN));
-    private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, "_delta");
-    private File mCurrentSetFullDir = new File(mCurrentSetDir, "_full");
+    private File mCurrentSetIncrementalDir = new File(mCurrentSetDir, INCREMENTAL_DIR);
+    private File mCurrentSetFullDir = new File(mCurrentSetDir, FULL_DATA_DIR);
 
     private PackageInfo[] mRestorePackages = null;
     private int mRestorePackage = -1;  // Index into mRestorePackages
-    private File mRestoreDataDir;
+    private int mRestoreType;
+    private File mRestoreSetDir;
+    private File mRestoreSetIncrementalDir;
+    private File mRestoreSetFullDir;
     private long mRestoreToken;
 
     // Additional bookkeeping for full backup
@@ -86,6 +93,9 @@
 
     private File mFullRestoreSetDir;
     private HashSet<String> mFullRestorePackages;
+    private FileInputStream mCurFullRestoreStream;
+    private FileOutputStream mFullRestoreSocketStream;
+    private byte[] mFullRestoreBuffer;
 
     public LocalTransport(Context context) {
         mContext = context;
@@ -97,34 +107,41 @@
         }
     }
 
+    @Override
     public String name() {
         return new ComponentName(mContext, this.getClass()).flattenToShortString();
     }
 
+    @Override
     public Intent configurationIntent() {
         // The local transport is not user-configurable
         return null;
     }
 
+    @Override
     public String currentDestinationString() {
         return TRANSPORT_DESTINATION_STRING;
     }
 
+    @Override
     public String transportDirName() {
         return TRANSPORT_DIR_NAME;
     }
 
+    @Override
     public long requestBackupTime() {
         // any time is a good time for local backup
         return 0;
     }
 
+    @Override
     public int initializeDevice() {
         if (DEBUG) Log.v(TAG, "wiping all data");
         deleteContents(mCurrentSetDir);
-        return BackupTransport.TRANSPORT_OK;
+        return TRANSPORT_OK;
     }
 
+    @Override
     public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) {
         if (DEBUG) {
             try {
@@ -184,7 +201,7 @@
                         entity.write(buf, 0, dataSize);
                     } catch (IOException e) {
                         Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
-                        return BackupTransport.TRANSPORT_ERROR;
+                        return TRANSPORT_ERROR;
                     } finally {
                         entity.close();
                     }
@@ -192,11 +209,11 @@
                     entityFile.delete();
                 }
             }
-            return BackupTransport.TRANSPORT_OK;
+            return TRANSPORT_OK;
         } catch (IOException e) {
             // oops, something went wrong.  abort the operation and return error.
             Log.v(TAG, "Exception reading backup input:", e);
-            return BackupTransport.TRANSPORT_ERROR;
+            return TRANSPORT_ERROR;
         }
     }
 
@@ -215,6 +232,7 @@
         }
     }
 
+    @Override
     public int clearBackupData(PackageInfo packageInfo) {
         if (DEBUG) Log.v(TAG, "clearBackupData() pkg=" + packageInfo.packageName);
 
@@ -236,9 +254,10 @@
             packageDir.delete();
         }
 
-        return BackupTransport.TRANSPORT_OK;
+        return TRANSPORT_OK;
     }
 
+    @Override
     public int finishBackup() {
         if (DEBUG) Log.v(TAG, "finishBackup()");
         if (mSocket != null) {
@@ -252,24 +271,27 @@
                 mFullTargetPackage = null;
                 mSocket.close();
             } catch (IOException e) {
-                return BackupTransport.TRANSPORT_ERROR;
+                return TRANSPORT_ERROR;
             } finally {
                 mSocket = null;
             }
         }
-        return BackupTransport.TRANSPORT_OK;
+        return TRANSPORT_OK;
     }
 
     // ------------------------------------------------------------------------------------
     // Full backup handling
+
+    @Override
     public long requestFullBackupTime() {
         return 0;
     }
 
+    @Override
     public int performFullBackup(PackageInfo targetPackage, ParcelFileDescriptor socket) {
         if (mSocket != null) {
             Log.e(TAG, "Attempt to initiate full backup while one is in progress");
-            return BackupTransport.TRANSPORT_ERROR;
+            return TRANSPORT_ERROR;
         }
 
         if (DEBUG) {
@@ -284,7 +306,7 @@
             mSocketInputStream = new FileInputStream(mSocket.getFileDescriptor());
         } catch (IOException e) {
             Log.e(TAG, "Unable to process socket for full backup");
-            return BackupTransport.TRANSPORT_ERROR;
+            return TRANSPORT_ERROR;
         }
 
         mFullTargetPackage = targetPackage.packageName;
@@ -293,18 +315,19 @@
             File tarball = new File(mCurrentSetFullDir, mFullTargetPackage);
             tarstream = new FileOutputStream(tarball);
         } catch (FileNotFoundException e) {
-            return BackupTransport.TRANSPORT_ERROR;
+            return TRANSPORT_ERROR;
         }
         mFullBackupOutputStream = new BufferedOutputStream(tarstream);
         mFullBackupBuffer = new byte[4096];
 
-        return BackupTransport.TRANSPORT_OK;
+        return TRANSPORT_OK;
     }
 
+    @Override
     public int sendBackupData(int numBytes) {
         if (mFullBackupBuffer == null) {
             Log.w(TAG, "Attempted sendBackupData before performFullBackup");
-            return BackupTransport.TRANSPORT_ERROR;
+            return TRANSPORT_ERROR;
         }
 
         if (numBytes > mFullBackupBuffer.length) {
@@ -316,21 +339,23 @@
             if (nRead < 0) {
                 // Something went wrong if we expect data but saw EOD
                 Log.w(TAG, "Unexpected EOD; failing backup");
-                return BackupTransport.TRANSPORT_ERROR;
+                return TRANSPORT_ERROR;
             }
             mFullBackupOutputStream.write(mFullBackupBuffer, 0, nRead);
             numBytes -= nRead;
             } catch (IOException e) {
                 Log.e(TAG, "Error handling backup data for " + mFullTargetPackage);
-                return BackupTransport.TRANSPORT_ERROR;
+                return TRANSPORT_ERROR;
             }
         }
-        return BackupTransport.TRANSPORT_OK;
+        return TRANSPORT_OK;
     }
 
     // ------------------------------------------------------------------------------------
     // Restore handling
     static final long[] POSSIBLE_SETS = { 2, 3, 4, 5, 6, 7, 8, 9 }; 
+
+    @Override
     public RestoreSet[] getAvailableRestoreSets() {
         long[] existing = new long[POSSIBLE_SETS.length + 1];
         int num = 0;
@@ -351,40 +376,69 @@
         return available;
     }
 
+    @Override
     public long getCurrentRestoreSet() {
         // The current restore set always has the same token
         return CURRENT_SET_TOKEN;
     }
 
+    @Override
     public int startRestore(long token, PackageInfo[] packages) {
         if (DEBUG) Log.v(TAG, "start restore " + token);
         mRestorePackages = packages;
         mRestorePackage = -1;
         mRestoreToken = token;
-        mRestoreDataDir = new File(mDataDir, Long.toString(token));
-        return BackupTransport.TRANSPORT_OK;
+        mRestoreSetDir = new File(mDataDir, Long.toString(token));
+        mRestoreSetIncrementalDir = new File(mRestoreSetDir, INCREMENTAL_DIR);
+        mRestoreSetFullDir = new File(mRestoreSetDir, FULL_DATA_DIR);
+        return TRANSPORT_OK;
     }
 
-    public String nextRestorePackage() {
+    @Override
+    public RestoreDescription nextRestorePackage() {
         if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
+
+        boolean found = false;
         while (++mRestorePackage < mRestorePackages.length) {
             String name = mRestorePackages[mRestorePackage].packageName;
+
+            // If we have key/value data for this package, deliver that
             // skip packages where we have a data dir but no actual contents
-            String[] contents = (new File(mRestoreDataDir, name)).list();
+            String[] contents = (new File(mRestoreSetIncrementalDir, name)).list();
             if (contents != null && contents.length > 0) {
-                if (DEBUG) Log.v(TAG, "  nextRestorePackage() = " + name);
-                return name;
+                if (DEBUG) Log.v(TAG, "  nextRestorePackage(TYPE_KEY_VALUE) = " + name);
+                mRestoreType = RestoreDescription.TYPE_KEY_VALUE;
+                found = true;
+            }
+
+            if (!found) {
+                // No key/value data; check for [non-empty] full data
+                File maybeFullData = new File(mRestoreSetFullDir, name);
+                if (maybeFullData.length() > 0) {
+                    if (DEBUG) Log.v(TAG, "  nextRestorePackage(TYPE_FULL_STREAM) = " + name);
+                    mRestoreType = RestoreDescription.TYPE_FULL_STREAM;
+                    mCurFullRestoreStream = null;   // ensure starting from the ground state
+                    found = true;
+                }
+            }
+
+            if (found) {
+                return new RestoreDescription(name, mRestoreType);
             }
         }
 
         if (DEBUG) Log.v(TAG, "  no more packages to restore");
-        return "";
+        return RestoreDescription.NO_MORE_PACKAGES;
     }
 
+    @Override
     public int getRestoreData(ParcelFileDescriptor outFd) {
         if (mRestorePackages == null) throw new IllegalStateException("startRestore not called");
         if (mRestorePackage < 0) throw new IllegalStateException("nextRestorePackage not called");
-        File packageDir = new File(mRestoreDataDir, mRestorePackages[mRestorePackage].packageName);
+        if (mRestoreType != RestoreDescription.TYPE_KEY_VALUE) {
+            throw new IllegalStateException("getRestoreData(fd) for non-key/value dataset");
+        }
+        File packageDir = new File(mRestoreSetDir, mRestorePackages[mRestorePackage].packageName);
 
         // The restore set is the concatenation of the individual record blobs,
         // each of which is a file in the package's directory.  We return the
@@ -394,7 +448,7 @@
         ArrayList<DecodedFilename> blobs = contentsByKey(packageDir);
         if (blobs == null) {  // nextRestorePackage() ensures the dir exists, so this is an error
             Log.e(TAG, "No keys for package: " + packageDir);
-            return BackupTransport.TRANSPORT_ERROR;
+            return TRANSPORT_ERROR;
         }
 
         // We expect at least some data if the directory exists in the first place
@@ -415,10 +469,10 @@
                     in.close();
                 }
             }
-            return BackupTransport.TRANSPORT_OK;
+            return TRANSPORT_OK;
         } catch (IOException e) {
             Log.e(TAG, "Unable to read backup records", e);
-            return BackupTransport.TRANSPORT_ERROR;
+            return TRANSPORT_ERROR;
         }
     }
 
@@ -455,38 +509,27 @@
         return contents;
     }
 
+    @Override
     public void finishRestore() {
         if (DEBUG) Log.v(TAG, "finishRestore()");
+        if (mRestoreType == RestoreDescription.TYPE_FULL_STREAM) {
+            resetFullRestoreState();
+        }
+        mRestoreType = 0;
     }
 
     // ------------------------------------------------------------------------------------
     // Full restore handling
 
-    public int prepareFullRestore(long token, String[] targetPackages) {
-        mRestoreDataDir = new File(mDataDir, Long.toString(token));
-        mFullRestoreSetDir = new File(mRestoreDataDir, "_full");
-        mFullRestorePackages = new HashSet<String>();
-        if (mFullRestoreSetDir.exists()) {
-            List<String> pkgs = Arrays.asList(mFullRestoreSetDir.list());
-            HashSet<String> available = new HashSet<String>(pkgs);
-
-            for (int i = 0; i < targetPackages.length; i++) {
-                if (available.contains(targetPackages[i])) {
-                    mFullRestorePackages.add(targetPackages[i]);
-                }
-            }
+    private void resetFullRestoreState() {
+        try {
+        mCurFullRestoreStream.close();
+        } catch (IOException e) {
+            Log.w(TAG, "Unable to close full restore input stream");
         }
-        return BackupTransport.TRANSPORT_OK;
-    }
-
-    /**
-     * Ask the transport what package's full data will be restored next.  When all apps'
-     * data has been delivered, the transport should return {@code null} here.
-     * @return The package name of the next application whose data will be restored, or
-     *    {@code null} if all available package has been delivered.
-     */
-    public String getNextFullRestorePackage() {
-        return null;
+        mCurFullRestoreStream = null;
+        mFullRestoreSocketStream = null;
+        mFullRestoreBuffer = null;
     }
 
     /**
@@ -511,7 +554,79 @@
      *    indicating a fatal error condition that precludes further restore operations
      *    on the current dataset.
      */
+    @Override
     public int getNextFullRestoreDataChunk(ParcelFileDescriptor socket) {
-        return 0;
+        if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
+            throw new IllegalStateException("Asked for full restore data for non-stream package");
+        }
+
+        // first chunk?
+        if (mCurFullRestoreStream == null) {
+            final String name = mRestorePackages[mRestorePackage].packageName;
+            if (DEBUG) Log.i(TAG, "Starting full restore of " + name);
+            File dataset = new File(mRestoreSetFullDir, name);
+            try {
+                mCurFullRestoreStream = new FileInputStream(dataset);
+            } catch (IOException e) {
+                // If we can't open the target package's tarball, we return the single-package
+                // error code and let the caller go on to the next package.
+                Log.e(TAG, "Unable to read archive for " + name);
+                return TRANSPORT_PACKAGE_REJECTED;
+            }
+            mFullRestoreSocketStream = new FileOutputStream(socket.getFileDescriptor());
+            mFullRestoreBuffer = new byte[32*1024];
+        }
+
+        int nRead;
+        try {
+            nRead = mCurFullRestoreStream.read(mFullRestoreBuffer);
+            if (nRead < 0) {
+                // EOF: tell the caller we're done
+                nRead = NO_MORE_DATA;
+            } else if (nRead == 0) {
+                // This shouldn't happen when reading a FileInputStream; we should always
+                // get either a positive nonzero byte count or -1.  Log the situation and
+                // treat it as EOF.
+                Log.w(TAG, "read() of archive file returned 0; treating as EOF");
+                nRead = NO_MORE_DATA;
+            } else {
+                if (DEBUG) {
+                    Log.i(TAG, "   delivering restore chunk: " + nRead);
+                }
+                mFullRestoreSocketStream.write(mFullRestoreBuffer, 0, nRead);
+            }
+        } catch (IOException e) {
+            return TRANSPORT_ERROR;  // Hard error accessing the file; shouldn't happen
+        } finally {
+            // Most transports will need to explicitly close 'socket' here, but this transport
+            // is in the same process as the caller so it can leave it up to the backup manager
+            // to manage both socket fds.
+        }
+
+        return nRead;
     }
+
+    /**
+     * If the OS encounters an error while processing {@link RestoreDescription#TYPE_FULL_STREAM}
+     * data for restore, it will invoke this method to tell the transport that it should
+     * abandon the data download for the current package.  The OS will then either call
+     * {@link #nextRestorePackage()} again to move on to restoring the next package in the
+     * set being iterated over, or will call {@link #finishRestore()} to shut down the restore
+     * operation.
+     *
+     * @return {@link #TRANSPORT_OK} if the transport was successful in shutting down the
+     *    current stream cleanly, or {@link #TRANSPORT_ERROR} to indicate a serious
+     *    transport-level failure.  If the transport reports an error here, the entire restore
+     *    operation will immediately be finished with no further attempts to restore app data.
+     */
+    @Override
+    public int abortFullRestore() {
+        if (mRestoreType != RestoreDescription.TYPE_FULL_STREAM) {
+            throw new IllegalStateException("abortFullRestore() but not currently restoring");
+        }
+        resetFullRestoreState();
+        mRestoreType = 0;
+        return TRANSPORT_OK;
+    }
+
 }
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 240d520..02e4b3f 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -24,6 +24,7 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.NetworkStats;
+import android.net.wifi.WifiManager;
 import android.os.BadParcelableException;
 import android.os.BatteryManager;
 import android.os.BatteryStats;
@@ -88,7 +89,7 @@
     private static final int MAGIC = 0xBA757475; // 'BATSTATS'
 
     // Current on-disk Parcel version
-    private static final int VERSION = 106 + (USE_OLD_HISTORY ? 1000 : 0);
+    private static final int VERSION = 107 + (USE_OLD_HISTORY ? 1000 : 0);
 
     // Maximum number of items we will record in the history.
     private static final int MAX_HISTORY_ITEMS = 2000;
@@ -284,6 +285,13 @@
     int mWifiState = -1;
     final StopwatchTimer[] mWifiStateTimer = new StopwatchTimer[NUM_WIFI_STATES];
 
+    int mWifiSupplState = -1;
+    final StopwatchTimer[] mWifiSupplStateTimer = new StopwatchTimer[NUM_WIFI_SUPPL_STATES];
+
+    int mWifiSignalStrengthBin = -1;
+    final StopwatchTimer[] mWifiSignalStrengthsTimer =
+            new StopwatchTimer[NUM_WIFI_SIGNAL_STRENGTH_BINS];
+
     boolean mBluetoothOn;
     StopwatchTimer mBluetoothOnTimer;
 
@@ -291,6 +299,7 @@
     final StopwatchTimer[] mBluetoothStateTimer = new StopwatchTimer[NUM_BLUETOOTH_STATES];
 
     int mMobileRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
+    long mMobileRadioActiveStartTime;
     StopwatchTimer mMobileRadioActiveTimer;
     StopwatchTimer mMobileRadioActivePerAppTimer;
     LongSamplingCounter mMobileRadioActiveAdjustedTime;
@@ -1425,10 +1434,6 @@
             return 0;
         }
 
-        long getLastUpdateTimeMs() {
-            return mUpdateTime;
-        }
-
         void stopRunningLocked(long elapsedRealtimeMs) {
             // Ignore attempt to stop a timer that isn't running
             if (mNesting == 0) {
@@ -2089,7 +2094,9 @@
             if (mHistoryLastWritten.batteryLevel == cur.batteryLevel &&
                     (dataSize >= MAX_MAX_HISTORY_BUFFER
                             || ((mHistoryLastWritten.states^cur.states)
-                                    & HistoryItem.MOST_INTERESTING_STATES) == 0)) {
+                                    & HistoryItem.MOST_INTERESTING_STATES) == 0
+                            || ((mHistoryLastWritten.states2^cur.states2)
+                                    & HistoryItem.MOST_INTERESTING_STATES2) == 0)) {
                 return;
             }
 
@@ -2790,11 +2797,11 @@
                     powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_MEDIUM
                             || powerState == DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH;
             if (active) {
-                realElapsedRealtimeMs = elapsedRealtime;
+                mMobileRadioActiveStartTime = realElapsedRealtimeMs = elapsedRealtime;
                 mHistoryCur.states |= HistoryItem.STATE_MOBILE_RADIO_ACTIVE_FLAG;
             } else {
                 realElapsedRealtimeMs = timestampNs / (1000*1000);
-                long lastUpdateTimeMs = mMobileRadioActiveTimer.getLastUpdateTimeMs();
+                long lastUpdateTimeMs = mMobileRadioActiveStartTime;
                 if (realElapsedRealtimeMs < lastUpdateTimeMs) {
                     Slog.wtf(TAG, "Data connection inactive timestamp " + realElapsedRealtimeMs
                             + " is before start time " + lastUpdateTimeMs);
@@ -2866,7 +2873,7 @@
         }
     }
 
-    void stopAllSignalStrengthTimersLocked(int except) {
+    void stopAllPhoneSignalStrengthTimersLocked(int except) {
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         for (int i = 0; i < SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
             if (i == except) {
@@ -2962,13 +2969,13 @@
                 if (!mPhoneSignalStrengthsTimer[strengthBin].isRunningLocked()) {
                     mPhoneSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtime);
                 }
-                mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_SIGNAL_STRENGTH_MASK)
-                        | (strengthBin << HistoryItem.STATE_SIGNAL_STRENGTH_SHIFT);
+                mHistoryCur.states = (mHistoryCur.states&~HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_MASK)
+                        | (strengthBin << HistoryItem.STATE_PHONE_SIGNAL_STRENGTH_SHIFT);
                 if (DEBUG_HISTORY) Slog.v(TAG, "Signal strength " + strengthBin + " to: "
                         + Integer.toHexString(mHistoryCur.states));
                 newHistory = true;
             } else {
-                stopAllSignalStrengthTimersLocked(-1);
+                stopAllPhoneSignalStrengthTimersLocked(-1);
             }
             mPhoneSignalStrengthBin = strengthBin;
         }
@@ -3068,7 +3075,7 @@
         if (!mWifiOn) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
-            mHistoryCur.states |= HistoryItem.STATE_WIFI_ON_FLAG;
+            mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI on to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -3081,7 +3088,7 @@
         final long elapsedRealtime = SystemClock.elapsedRealtime();
         final long uptime = SystemClock.uptimeMillis();
         if (mWifiOn) {
-            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_ON_FLAG;
+            mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_ON_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI off to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -3174,7 +3181,7 @@
         if (!mGlobalWifiRunning) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
-            mHistoryCur.states |= HistoryItem.STATE_WIFI_RUNNING_FLAG;
+            mHistoryCur.states2 |= HistoryItem.STATE2_WIFI_RUNNING_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI running to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -3212,7 +3219,7 @@
         if (mGlobalWifiRunning) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
             final long uptime = SystemClock.uptimeMillis();
-            mHistoryCur.states &= ~HistoryItem.STATE_WIFI_RUNNING_FLAG;
+            mHistoryCur.states2 &= ~HistoryItem.STATE2_WIFI_RUNNING_FLAG;
             if (DEBUG_HISTORY) Slog.v(TAG, "WIFI stopped to: "
                     + Integer.toHexString(mHistoryCur.states));
             addHistoryRecordLocked(elapsedRealtime, uptime);
@@ -3240,6 +3247,64 @@
         }
     }
 
+    public void noteWifiSupplicantStateChangedLocked(int supplState, boolean failedAuth) {
+        if (DEBUG) Log.i(TAG, "WiFi suppl state -> " + supplState);
+        if (mWifiSupplState != supplState) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            if (mWifiSupplState >= 0) {
+                mWifiSupplStateTimer[mWifiSupplState].stopRunningLocked(elapsedRealtime);
+            }
+            mWifiSupplState = supplState;
+            mWifiSupplStateTimer[supplState].startRunningLocked(elapsedRealtime);
+            mHistoryCur.states2 =
+                    (mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SUPPL_STATE_MASK)
+                    | (supplState << HistoryItem.STATE2_WIFI_SUPPL_STATE_SHIFT);
+            if (DEBUG_HISTORY) Slog.v(TAG, "Wifi suppl state " + supplState + " to: "
+                    + Integer.toHexString(mHistoryCur.states2));
+            addHistoryRecordLocked(elapsedRealtime, uptime);
+        }
+    }
+
+    void stopAllWifiSignalStrengthTimersLocked(int except) {
+        final long elapsedRealtime = SystemClock.elapsedRealtime();
+        for (int i = 0; i < NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            if (i == except) {
+                continue;
+            }
+            while (mWifiSignalStrengthsTimer[i].isRunningLocked()) {
+                mWifiSignalStrengthsTimer[i].stopRunningLocked(elapsedRealtime);
+            }
+        }
+    }
+
+    public void noteWifiRssiChangedLocked(int newRssi) {
+        int strengthBin = WifiManager.calculateSignalLevel(newRssi, NUM_WIFI_SIGNAL_STRENGTH_BINS);
+        if (DEBUG) Log.i(TAG, "WiFi rssi -> " + newRssi + " bin=" + strengthBin);
+        if (mWifiSignalStrengthBin != strengthBin) {
+            final long elapsedRealtime = SystemClock.elapsedRealtime();
+            final long uptime = SystemClock.uptimeMillis();
+            if (mWifiSignalStrengthBin >= 0) {
+                mWifiSignalStrengthsTimer[mWifiSignalStrengthBin].stopRunningLocked(
+                        elapsedRealtime);
+            }
+            if (strengthBin >= 0) {
+                if (!mWifiSignalStrengthsTimer[strengthBin].isRunningLocked()) {
+                    mWifiSignalStrengthsTimer[strengthBin].startRunningLocked(elapsedRealtime);
+                }
+                mHistoryCur.states2 =
+                        (mHistoryCur.states2&~HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_MASK)
+                        | (strengthBin << HistoryItem.STATE2_WIFI_SIGNAL_STRENGTH_SHIFT);
+                if (DEBUG_HISTORY) Slog.v(TAG, "Wifi signal strength " + strengthBin + " to: "
+                        + Integer.toHexString(mHistoryCur.states2));
+                addHistoryRecordLocked(elapsedRealtime, uptime);
+            } else {
+                stopAllWifiSignalStrengthTimersLocked(-1);
+            }
+            mWifiSignalStrengthBin = strengthBin;
+        }
+    }
+
     public void noteBluetoothOnLocked() {
         if (!mBluetoothOn) {
             final long elapsedRealtime = SystemClock.elapsedRealtime();
@@ -3583,6 +3648,26 @@
         return mWifiStateTimer[wifiState].getCountLocked(which);
     }
 
+    @Override public long getWifiSupplStateTime(int state,
+            long elapsedRealtimeUs, int which) {
+        return mWifiSupplStateTimer[state].getTotalTimeLocked(
+                elapsedRealtimeUs, which);
+    }
+
+    @Override public int getWifiSupplStateCount(int state, int which) {
+        return mWifiSupplStateTimer[state].getCountLocked(which);
+    }
+
+    @Override public long getWifiSignalStrengthTime(int strengthBin,
+            long elapsedRealtimeUs, int which) {
+        return mWifiSignalStrengthsTimer[strengthBin].getTotalTimeLocked(
+                elapsedRealtimeUs, which);
+    }
+
+    @Override public int getWifiSignalStrengthCount(int strengthBin, int which) {
+        return mWifiSignalStrengthsTimer[strengthBin].getCountLocked(which);
+    }
+
     @Override public long getBluetoothOnTime(long elapsedRealtimeUs, int which) {
         return mBluetoothOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which);
     }
@@ -5583,6 +5668,13 @@
         for (int i=0; i<NUM_WIFI_STATES; i++) {
             mWifiStateTimer[i] = new StopwatchTimer(null, -600-i, null, mOnBatteryTimeBase);
         }
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            mWifiSupplStateTimer[i] = new StopwatchTimer(null, -700-i, null, mOnBatteryTimeBase);
+        }
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            mWifiSignalStrengthsTimer[i] = new StopwatchTimer(null, -800-i, null,
+                    mOnBatteryTimeBase);
+        }
         mBluetoothOnTimer = new StopwatchTimer(null, -6, null, mOnBatteryTimeBase);
         for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
             mBluetoothStateTimer[i] = new StopwatchTimer(null, -500-i, null, mOnBatteryTimeBase);
@@ -5859,6 +5951,12 @@
         for (int i=0; i<NUM_WIFI_STATES; i++) {
             mWifiStateTimer[i].reset(false);
         }
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            mWifiSupplStateTimer[i].reset(false);
+        }
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            mWifiSignalStrengthsTimer[i].reset(false);
+        }
         mBluetoothOnTimer.reset(false);
         for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
             mBluetoothStateTimer[i].reset(false);
@@ -7015,6 +7113,12 @@
         for (int i=0; i<NUM_WIFI_STATES; i++) {
             mWifiStateTimer[i].readSummaryFromParcelLocked(in);
         }
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            mWifiSupplStateTimer[i].readSummaryFromParcelLocked(in);
+        }
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            mWifiSignalStrengthsTimer[i].readSummaryFromParcelLocked(in);
+        }
         mBluetoothOn = false;
         mBluetoothOnTimer.readSummaryFromParcelLocked(in);
         for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
@@ -7267,6 +7371,12 @@
         for (int i=0; i<NUM_WIFI_STATES; i++) {
             mWifiStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         }
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            mWifiSupplStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+        }
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            mWifiSignalStrengthsTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
+        }
         mBluetoothOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS);
         for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
             mBluetoothStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS);
@@ -7539,6 +7649,14 @@
             mWifiStateTimer[i] = new StopwatchTimer(null, -600-i,
                     null, mOnBatteryTimeBase, in);
         }
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            mWifiSupplStateTimer[i] = new StopwatchTimer(null, -700-i,
+                    null, mOnBatteryTimeBase, in);
+        }
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            mWifiSignalStrengthsTimer[i] = new StopwatchTimer(null, -800-i,
+                    null, mOnBatteryTimeBase, in);
+        }
         mBluetoothOn = false;
         mBluetoothOnTimer = new StopwatchTimer(null, -6, null, mOnBatteryTimeBase, in);
         for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
@@ -7670,6 +7788,12 @@
         for (int i=0; i<NUM_WIFI_STATES; i++) {
             mWifiStateTimer[i].writeToParcel(out, uSecRealtime);
         }
+        for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+            mWifiSupplStateTimer[i].writeToParcel(out, uSecRealtime);
+        }
+        for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+            mWifiSignalStrengthsTimer[i].writeToParcel(out, uSecRealtime);
+        }
         mBluetoothOnTimer.writeToParcel(out, uSecRealtime);
         for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
             mBluetoothStateTimer[i].writeToParcel(out, uSecRealtime);
@@ -7771,7 +7895,7 @@
             pr.println("*** Phone timer:");
             mPhoneOnTimer.logState(pr, "  ");
             for (int i=0; i<SignalStrength.NUM_SIGNAL_STRENGTH_BINS; i++) {
-                pr.println("*** Signal strength #" + i + ":");
+                pr.println("*** Phone signal strength #" + i + ":");
                 mPhoneSignalStrengthsTimer[i].logState(pr, "  ");
             }
             pr.println("*** Signal scanning :");
@@ -7793,6 +7917,14 @@
                 pr.println("*** Wifi state #" + i + ":");
                 mWifiStateTimer[i].logState(pr, "  ");
             }
+            for (int i=0; i<NUM_WIFI_SUPPL_STATES; i++) {
+                pr.println("*** Wifi suppl state #" + i + ":");
+                mWifiSupplStateTimer[i].logState(pr, "  ");
+            }
+            for (int i=0; i<NUM_WIFI_SIGNAL_STRENGTH_BINS; i++) {
+                pr.println("*** Wifi signal strength #" + i + ":");
+                mWifiSignalStrengthsTimer[i].logState(pr, "  ");
+            }
             pr.println("*** Bluetooth timer:");
             mBluetoothOnTimer.logState(pr, "  ");
             for (int i=0; i< NUM_BLUETOOTH_STATES; i++) {
diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java
index 58cd60d..e58d68f 100644
--- a/core/java/com/android/internal/os/ProcessCpuTracker.java
+++ b/core/java/com/android/internal/os/ProcessCpuTracker.java
@@ -442,8 +442,9 @@
                 final String[] procStatsString = mProcessFullStatsStringData;
                 final long[] procStats = mProcessFullStatsData;
                 st.base_uptime = SystemClock.uptimeMillis();
-                if (Process.readProcFile(st.statFile.toString(),
-                        PROCESS_FULL_STATS_FORMAT, procStatsString,
+                String path = st.statFile.toString();
+                //Slog.d(TAG, "Reading proc file: " + path);
+                if (Process.readProcFile(path, PROCESS_FULL_STATS_FORMAT, procStatsString,
                         procStats, null)) {
                     // This is a possible way to filter out processes that
                     // are actually kernel threads...  do we want to?  Some
diff --git a/core/java/com/android/internal/policy/IFaceLockInterface.aidl b/core/java/com/android/internal/policy/IFaceLockInterface.aidl
index 017801b..bc1f002 100644
--- a/core/java/com/android/internal/policy/IFaceLockInterface.aidl
+++ b/core/java/com/android/internal/policy/IFaceLockInterface.aidl
@@ -23,6 +23,7 @@
     void startUi(IBinder containingWindowToken, int x, int y, int width, int height,
             boolean useLiveliness);
     void stopUi();
+    void startWithoutUi();
     void registerCallback(IFaceLockCallback cb);
     void unregisterCallback(IFaceLockCallback cb);
 }
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index a01e9b7..84bd443 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -24,9 +24,6 @@
 {
     void setIcon(int index, in StatusBarIcon icon);
     void removeIcon(int index);
-    void addNotification(in StatusBarNotification notification);
-    void updateNotification(in StatusBarNotification notification);
-    void removeNotification(String key);
     void disable(int state);
     void animateExpandNotificationsPanel();
     void animateExpandSettingsPanel();
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index a3b417f..f3430e7 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -39,8 +39,7 @@
     // ---- Methods below are for use by the status bar policy services ----
     // You need the STATUS_BAR_SERVICE permission
     void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList,
-            out List<StatusBarNotification> notifications, out int[] switches,
-            out List<IBinder> binders);
+            out int[] switches, out List<IBinder> binders);
     void onPanelRevealed();
     void onPanelHidden();
     void onNotificationClick(String key);
diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java
index 002573e..97b1634 100644
--- a/core/java/com/android/internal/widget/SwipeDismissLayout.java
+++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java
@@ -45,10 +45,9 @@
         /**
          * Called when the layout has been swiped and the position of the window should change.
          *
-         * @param progress A number in [-1, 1] representing how far to the left
-         * or right the window has been swiped. Negative values are swipes
-         * left, and positives are right.
-         * @param translate A number in [-w, w], where w is the width of the
+         * @param progress A number in [0, 1] representing how far to the
+         * right the window has been swiped
+         * @param translate A number in [0, w], where w is the width of the
          * layout. This is equivalent to progress * layout.getWidth().
          */
         void onSwipeProgressChanged(SwipeDismissLayout layout, float progress, float translate);
@@ -207,7 +206,7 @@
 
     private void setProgress(float deltaX) {
         mTranslationX = deltaX;
-        if (mProgressListener != null) {
+        if (mProgressListener != null && deltaX >= 0)  {
             mProgressListener.onSwipeProgressChanged(this, deltaX / getWidth(), deltaX);
         }
     }
diff --git a/core/java/com/android/server/SystemServiceManager.java b/core/java/com/android/server/SystemServiceManager.java
index 87a50a9..fda6479 100644
--- a/core/java/com/android/server/SystemServiceManager.java
+++ b/core/java/com/android/server/SystemServiceManager.java
@@ -50,8 +50,19 @@
      * @return The service instance.
      */
     @SuppressWarnings("unchecked")
-    public SystemService startService(String className) throws ClassNotFoundException {
-        return startService((Class<SystemService>) Class.forName(className));
+    public SystemService startService(String className) {
+        final Class<SystemService> serviceClass;
+        try {
+            serviceClass = (Class<SystemService>)Class.forName(className);
+        } catch (ClassNotFoundException ex) {
+            Slog.i(TAG, "Starting " + className);
+            throw new RuntimeException("Failed to create service " + className
+                    + ": service class not found, usually indicates that the caller should "
+                    + "have called PackageManager.hasSystemFeature() to check whether the "
+                    + "feature is available on this device before trying to start the "
+                    + "services that implement it", ex);
+        }
+        return startService(serviceClass);
     }
 
     /**
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 15dfed1..cb00062 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -89,7 +89,7 @@
 	android_util_Process.cpp \
 	android_util_StringBlock.cpp \
 	android_util_XmlBlock.cpp \
-	android/graphics/AndroidPicture.cpp \
+	android_graphics_Picture.cpp \
 	android/graphics/AutoDecodeCancel.cpp \
 	android/graphics/Bitmap.cpp \
 	android/graphics/BitmapFactory.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index a7a1faad..598d6c1 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -491,8 +491,8 @@
     char profile_duration[sizeof("-Xprofile-duration:") + PROPERTY_VALUE_MAX];
     char profile_interval[sizeof("-Xprofile-interval:") + PROPERTY_VALUE_MAX];
     char profile_backoff[sizeof("-Xprofile-backoff:") + PROPERTY_VALUE_MAX];
-    char profile_top_k_threshold[sizeof("-Xprofile-top-k-threshold") + PROPERTY_VALUE_MAX];
-    char profile_top_k_change_threshold[sizeof("-Xprofile-top-k-change-threshold") + PROPERTY_VALUE_MAX];
+    char profile_top_k_threshold[sizeof("-Xprofile-top-k-threshold:") + PROPERTY_VALUE_MAX];
+    char profile_top_k_change_threshold[sizeof("-Xprofile-top-k-change-threshold:") + PROPERTY_VALUE_MAX];
     char langOption[sizeof("-Duser.language=") + 3];
     char regionOption[sizeof("-Duser.region=") + 3];
     char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:") + sizeof(propBuf)];
diff --git a/core/jni/android/graphics/AndroidPicture.cpp b/core/jni/android/graphics/AndroidPicture.cpp
deleted file mode 100644
index 5977ab2..0000000
--- a/core/jni/android/graphics/AndroidPicture.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * Copyright (C) 2014 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.
- */
-
-#include "AndroidPicture.h"
-#include "SkCanvas.h"
-#include "SkStream.h"
-
-AndroidPicture::AndroidPicture(const AndroidPicture* src) {
-    if (NULL != src) {
-        mWidth = src->width();
-        mHeight = src->height();
-        if (NULL != src->mPicture.get()) {
-            mPicture.reset(SkRef(src->mPicture.get()));
-        } if (NULL != src->mRecorder.get()) {
-            mPicture.reset(src->makePartialCopy());
-        }
-    } else {
-        mWidth = 0;
-        mHeight = 0;
-    }
-}
-
-SkCanvas* AndroidPicture::beginRecording(int width, int height) {
-    mPicture.reset(NULL);
-    mRecorder.reset(new SkPictureRecorder);
-    mWidth = width;
-    mHeight = height;
-    return mRecorder->beginRecording(width, height, NULL, 0);
-}
-
-void AndroidPicture::endRecording() {
-    if (NULL != mRecorder.get()) {
-        mPicture.reset(mRecorder->endRecording());
-        mRecorder.reset(NULL);
-    }
-}
-
-int AndroidPicture::width() const {
-    if (NULL != mPicture.get()) {
-        SkASSERT(mPicture->width() == mWidth);
-        SkASSERT(mPicture->height() == mHeight);
-    }
-
-    return mWidth;
-}
-
-int AndroidPicture::height() const {
-    if (NULL != mPicture.get()) {
-        SkASSERT(mPicture->width() == mWidth);
-        SkASSERT(mPicture->height() == mHeight);
-    }
-
-    return mHeight;
-}
-
-AndroidPicture* AndroidPicture::CreateFromStream(SkStream* stream) {
-    AndroidPicture* newPict = new AndroidPicture;
-
-    newPict->mPicture.reset(SkPicture::CreateFromStream(stream));
-    if (NULL != newPict->mPicture.get()) {
-        newPict->mWidth = newPict->mPicture->width();
-        newPict->mHeight = newPict->mPicture->height();
-    }
-
-    return newPict;
-}
-
-void AndroidPicture::serialize(SkWStream* stream) const {
-    if (NULL != mRecorder.get()) {
-        SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy());
-        tempPict->serialize(stream);
-    } else if (NULL != mPicture.get()) {
-        mPicture->serialize(stream);
-    } else {
-        SkPicture empty;
-        empty.serialize(stream);
-    }
-}
-
-void AndroidPicture::draw(SkCanvas* canvas) {
-    if (NULL != mRecorder.get()) {
-        this->endRecording();
-        SkASSERT(NULL != mPicture.get());
-    }
-    if (NULL != mPicture.get()) {
-        // TODO: remove this const_cast once pictures are immutable
-        const_cast<SkPicture*>(mPicture.get())->draw(canvas);
-    }
-}
-
-SkPicture* AndroidPicture::makePartialCopy() const {
-    SkASSERT(NULL != mRecorder.get());
-
-    SkPictureRecorder reRecorder;
-
-    SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0);
-    mRecorder->partialReplay(canvas);
-    return reRecorder.endRecording();
-}
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index 9998995..c139c9d 100644
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -258,16 +258,16 @@
 
 // can return NULL
 static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
-    switch (src.config()) {
-        case SkBitmap::kARGB_8888_Config:
+    switch (src.colorType()) {
+        case kN32_SkColorType:
             if (src.isOpaque()) return ToColor_S32_Opaque;
             return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
-        case SkBitmap::kARGB_4444_Config:
+        case kARGB_4444_SkColorType:
             if (src.isOpaque()) return ToColor_S4444_Opaque;
             return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
-        case SkBitmap::kRGB_565_Config:
+        case kRGB_565_SkColorType:
             return ToColor_S565;
-        case SkBitmap::kIndex8_Config:
+        case kIndex_8_SkColorType:
             if (src.getColorTable() == NULL) {
                 return NULL;
             }
@@ -291,7 +291,7 @@
 static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
                               jint offset, jint stride, jint width, jint height,
                               jint configHandle, jboolean isMutable) {
-    SkBitmap::Config config = static_cast<SkBitmap::Config>(configHandle);
+    SkColorType colorType = SkBitmapConfigToColorType(static_cast<SkBitmap::Config>(configHandle));
     if (NULL != jColors) {
         size_t n = env->GetArrayLength(jColors);
         if (n < SkAbs32(stride) * (size_t)height) {
@@ -301,12 +301,12 @@
     }
 
     // ARGB_4444 is a deprecated format, convert automatically to 8888
-    if (config == SkBitmap::kARGB_4444_Config) {
-        config = SkBitmap::kARGB_8888_Config;
+    if (colorType == kARGB_4444_SkColorType) {
+        colorType = kN32_SkColorType;
     }
 
     SkBitmap bitmap;
-    bitmap.setConfig(config, width, height);
+    bitmap.setInfo(SkImageInfo::Make(width, height, colorType, kPremul_SkAlphaType));
 
     jbyteArray buff = GraphicsJNI::allocateJavaPixelRef(env, &bitmap, NULL);
     if (NULL == buff) {
@@ -515,28 +515,29 @@
 
     android::Parcel* p = android::parcelForJavaObject(env, parcel);
 
-    const bool              isMutable = p->readInt32() != 0;
-    const SkBitmap::Config  config = (SkBitmap::Config)p->readInt32();
-    const int               width = p->readInt32();
-    const int               height = p->readInt32();
-    const int               rowBytes = p->readInt32();
-    const int               density = p->readInt32();
+    const bool        isMutable = p->readInt32() != 0;
+    const SkColorType colorType = (SkColorType)p->readInt32();
+    const SkAlphaType alphaType = (SkAlphaType)p->readInt32();
+    const int         width = p->readInt32();
+    const int         height = p->readInt32();
+    const int         rowBytes = p->readInt32();
+    const int         density = p->readInt32();
 
-    if (SkBitmap::kARGB_8888_Config != config &&
-            SkBitmap::kRGB_565_Config != config &&
-            SkBitmap::kARGB_4444_Config != config &&
-            SkBitmap::kIndex8_Config != config &&
-            SkBitmap::kA8_Config != config) {
-        SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
+    if (kN32_SkColorType != colorType &&
+            kRGB_565_SkColorType != colorType &&
+            kARGB_4444_SkColorType != colorType &&
+            kIndex_8_SkColorType != colorType &&
+            kAlpha_8_SkColorType != colorType) {
+        SkDebugf("Bitmap_createFromParcel unknown colortype: %d\n", colorType);
         return NULL;
     }
 
     SkBitmap* bitmap = new SkBitmap;
 
-    bitmap->setConfig(config, width, height, rowBytes);
+    bitmap->setInfo(SkImageInfo::Make(width, height, colorType, alphaType), rowBytes);
 
     SkColorTable* ctable = NULL;
-    if (config == SkBitmap::kIndex8_Config) {
+    if (colorType == kIndex_8_SkColorType) {
         int count = p->readInt32();
         if (count > 0) {
             size_t size = count * sizeof(SkPMColor);
@@ -587,13 +588,14 @@
     android::Parcel* p = android::parcelForJavaObject(env, parcel);
 
     p->writeInt32(isMutable);
-    p->writeInt32(bitmap->config());
+    p->writeInt32(bitmap->colorType());
+    p->writeInt32(bitmap->alphaType());
     p->writeInt32(bitmap->width());
     p->writeInt32(bitmap->height());
     p->writeInt32(bitmap->rowBytes());
     p->writeInt32(density);
 
-    if (bitmap->config() == SkBitmap::kIndex8_Config) {
+    if (bitmap->colorType() == kIndex_8_SkColorType) {
         SkColorTable* ctable = bitmap->getColorTable();
         if (ctable != NULL) {
             int count = ctable->count();
diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp
index 5106f0d..86ed677 100644
--- a/core/jni/android/graphics/BitmapFactory.cpp
+++ b/core/jni/android/graphics/BitmapFactory.cpp
@@ -386,7 +386,7 @@
         // FIXME: If the alphaType is kUnpremul and the image has alpha, the
         // colors may not be correct, since Skia does not yet support drawing
         // to/from unpremultiplied bitmaps.
-        outputBitmap->setConfig(SkImageInfo::Make(scaledWidth, scaledHeight,
+        outputBitmap->setInfo(SkImageInfo::Make(scaledWidth, scaledHeight,
                 colorType, decodingBitmap.alphaType()));
         if (!outputBitmap->allocPixels(outputAllocator, NULL)) {
             return nullObjectReturn("allocation failed for scaled bitmap");
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 4584c46..9e09280 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -703,10 +703,11 @@
                                 jboolean hasAlpha, jlong paintHandle) {
         SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        SkImageInfo info = SkImageInfo::Make(width, height,
+                               hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+                               kPremul_SkAlphaType);
         SkBitmap    bitmap;
-        bitmap.setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config :
-                         SkBitmap::kRGB_565_Config, width, height);
-        if (!bitmap.allocPixels()) {
+        if (!bitmap.allocPixels(info)) {
             return;
         }
 
@@ -1123,29 +1124,82 @@
         delete[] posPtr;
     }
 
+#ifdef USE_MINIKIN
+    class DrawTextOnPathFunctor {
+    public:
+        DrawTextOnPathFunctor(const Layout& layout, SkCanvas* canvas, float hOffset,
+                    float vOffset, SkPaint* paint, SkPath* path)
+                : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
+                    paint(paint), path(path) {
+        }
+        void operator()(size_t start, size_t end) {
+            uint16_t glyphs[1];
+            for (size_t i = start; i < end; i++) {
+                glyphs[0] = layout.getGlyphId(i);
+                float x = hOffset + layout.getX(i);
+                float y = vOffset + layout.getY(i);
+                canvas->drawTextOnPathHV(glyphs, sizeof(glyphs), *path, x, y, *paint);
+            }
+        }
+    private:
+        const Layout& layout;
+        SkCanvas* canvas;
+        float hOffset;
+        float vOffset;
+        SkPaint* paint;
+        SkPath* path;
+    };
+#endif
+
+    static void doDrawTextOnPath(SkPaint* paint, const jchar* text, int count, int bidiFlags,
+            float hOffset, float vOffset, SkPath* path, SkCanvas* canvas, TypefaceImpl* typeface) {
+#ifdef USE_MINIKIN
+        Layout layout;
+        std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+        layout.doLayout(text, 0, count, count, css);
+        hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
+        // Set align to left for drawing, as we don't want individual
+        // glyphs centered or right-aligned; the offset above takes
+        // care of all alignment.
+        SkPaint::Align align = paint->getTextAlign();
+        paint->setTextAlign(SkPaint::kLeft_Align);
+
+        DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paint, path);
+        MinikinUtils::forFontRun(layout, paint, f);
+        paint->setTextAlign(align);
+#else
+        TextLayout::drawTextOnPath(paint, text, count, bidiFlags, hOffset, vOffset, path, canvas);
+#endif
+    }
+
     static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
             jlong canvasHandle, jcharArray text, jint index, jint count,
-            jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) {
+            jlong pathHandle, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
+            jlong typefaceHandle) {
         SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
 
         jchar* textArray = env->GetCharArrayElements(text, NULL);
-        TextLayout::drawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
-                                   path, canvas);
+        doDrawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
+                                   path, canvas, typeface);
         env->ReleaseCharArrayElements(text, textArray, 0);
     }
 
     static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
             jlong canvasHandle, jstring text, jlong pathHandle,
-            jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle) {
+            jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintHandle,
+            jlong typefaceHandle) {
         SkCanvas* canvas = getNativeCanvas(canvasHandle);
         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
         SkPaint* paint = reinterpret_cast<SkPaint*>(paintHandle);
+        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
+
         const jchar* text_ = env->GetStringChars(text, NULL);
         int count = env->GetStringLength(text);
-        TextLayout::drawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
-                                   path, canvas);
+        doDrawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
+                                   path, canvas, typeface);
         env->ReleaseStringChars(text, text_);
     }
 
@@ -1267,13 +1321,9 @@
         (void*) SkCanvasGlue::drawTextRun___CIIIIFFZPaintTypeface},
     {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V",
         (void*) SkCanvasGlue::drawTextRun__StringIIIIFFZPaintTypeface},
-    {"native_drawPosText","(J[CII[FJ)V",
-        (void*) SkCanvasGlue::drawPosText___CII_FPaint},
-    {"native_drawPosText","(JLjava/lang/String;[FJ)V",
-        (void*) SkCanvasGlue::drawPosText__String_FPaint},
-    {"native_drawTextOnPath","(J[CIIJFFIJ)V",
+    {"native_drawTextOnPath","(J[CIIJFFIJJ)V",
         (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
-    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJ)V",
+    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V",
         (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
 
     {"freeCaches", "()V", (void*) SkCanvasGlue::freeCaches},
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index a4337ccc..2bd7a28 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -3,7 +3,6 @@
 #include "jni.h"
 #include "JNIHelp.h"
 #include "GraphicsJNI.h"
-#include "AndroidPicture.h"
 
 #include "SkCanvas.h"
 #include "SkDevice.h"
@@ -346,17 +345,6 @@
     return p;
 }
 
-AndroidPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture)
-{
-    SkASSERT(env);
-    SkASSERT(picture);
-    SkASSERT(env->IsInstanceOf(picture, gPicture_class));
-    jlong pictureHandle = env->GetLongField(picture, gPicture_nativeInstanceID);
-    AndroidPicture* p = reinterpret_cast<AndroidPicture*>(pictureHandle);
-    SkASSERT(p);
-    return p;
-}
-
 SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
 {
     SkASSERT(env);
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 2e2f920..ad174f7 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -14,7 +14,6 @@
 class SkBitmapRegionDecoder;
 class SkCanvas;
 class SkPaint;
-class AndroidPicture;
 
 class GraphicsJNI {
 public:
@@ -50,7 +49,6 @@
     static SkPaint*  getNativePaint(JNIEnv*, jobject paint);
     static android::TypefaceImpl* getNativeTypeface(JNIEnv*, jobject paint);
     static SkBitmap* getNativeBitmap(JNIEnv*, jobject bitmap);
-    static AndroidPicture* getNativePicture(JNIEnv*, jobject picture);
     static SkRegion* getNativeRegion(JNIEnv*, jobject region);
 
     // Given the 'native' long held by the Rasterizer.java object, return a
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp
index a9360ea..fc92d53 100644
--- a/core/jni/android/graphics/MinikinUtils.cpp
+++ b/core/jni/android/graphics/MinikinUtils.cpp
@@ -19,6 +19,7 @@
 #include <string>
 
 #include "SkPaint.h"
+#include "SkPathMeasure.h"
 #include "minikin/Layout.h"
 #include "TypefaceImpl.h"
 #include "MinikinSkia.h"
@@ -76,4 +77,20 @@
     return 0;
 }
 
+float MinikinUtils::hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path) {
+    float align = 0;
+    switch (paint->getTextAlign()) {
+        case SkPaint::kCenter_Align:
+            align = -0.5f;
+            break;
+        case SkPaint::kRight_Align:
+            align = -1;
+            break;
+        default:
+            return 0;
+    }
+    SkPathMeasure measure(path, false);
+    return align * (layout.getAdvance() - measure.getLength());
+}
+
 }
diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h
index a96c6b1..b2662a1 100644
--- a/core/jni/android/graphics/MinikinUtils.h
+++ b/core/jni/android/graphics/MinikinUtils.h
@@ -36,6 +36,7 @@
 
     static float xOffsetForTextAlign(SkPaint* paint, const Layout& layout);
 
+    static float hOffsetForTextAlign(SkPaint* paint, const Layout& layout, const SkPath& path);
     // f is a functor of type void f(size_t start, size_t end);
     template <typename F>
     static void forFontRun(const Layout& layout, SkPaint* paint, F& f) {
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index d07b154..dc30814 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -37,6 +37,7 @@
 #include "TextLayout.h"
 
 #ifdef USE_MINIKIN
+#include <minikin/GraphemeBreak.h>
 #include <minikin/Layout.h>
 #include "MinikinSkia.h"
 #include "MinikinUtils.h"
@@ -778,6 +779,11 @@
 
     static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start,
             jint count, jint flags, jint offset, jint opt) {
+#ifdef USE_MINIKIN
+        GraphemeBreak::MoveOpt moveOpt = GraphemeBreak::MoveOpt(opt);
+        size_t result = GraphemeBreak::getTextRunCursor(text, start, count, offset, moveOpt);
+        return static_cast<jint>(result);
+#else
         jfloat scalarArray[count];
 
         TextLayout::getTextRunAdvances(paint, text, start, count, start + count, flags,
@@ -818,6 +824,7 @@
         }
 
         return pos;
+#endif
     }
 
     static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
diff --git a/core/jni/android/graphics/Picture.cpp b/core/jni/android/graphics/Picture.cpp
index 0683f73..bc0c25f 100644
--- a/core/jni/android/graphics/Picture.cpp
+++ b/core/jni/android/graphics/Picture.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2014 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.
@@ -14,123 +14,104 @@
  * limitations under the License.
  */
 
-#include "jni.h"
-#include "GraphicsJNI.h"
-#include <android_runtime/AndroidRuntime.h>
-#include "AndroidPicture.h"
+#include "Picture.h"
 
 #include "SkCanvas.h"
 #include "SkStream.h"
-#include "SkTemplates.h"
-#include "CreateJavaOutputStreamAdaptor.h"
 
 namespace android {
 
-class SkPictureGlue {
-public:
-    static jlong newPicture(JNIEnv* env, jobject, jlong srcHandle) {
-        const AndroidPicture* src = reinterpret_cast<AndroidPicture*>(srcHandle);
-        return reinterpret_cast<jlong>(new AndroidPicture(src));
-    }
-
-    static jlong deserialize(JNIEnv* env, jobject, jobject jstream,
-                             jbyteArray jstorage) {
-        AndroidPicture* picture = NULL;
-        SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage);
-        if (strm) {
-            picture = AndroidPicture::CreateFromStream(strm);
-            delete strm;
+Picture::Picture(const Picture* src) {
+    if (NULL != src) {
+        mWidth = src->width();
+        mHeight = src->height();
+        if (NULL != src->mPicture.get()) {
+            mPicture.reset(SkRef(src->mPicture.get()));
+        } if (NULL != src->mRecorder.get()) {
+            mPicture.reset(src->makePartialCopy());
         }
-        return reinterpret_cast<jlong>(picture);
+    } else {
+        mWidth = 0;
+        mHeight = 0;
     }
-
-    static void killPicture(JNIEnv* env, jobject, jlong pictureHandle) {
-        AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
-        SkASSERT(picture);
-        delete picture;
-    }
-
-    static void draw(JNIEnv* env, jobject, jlong canvasHandle,
-                            jlong pictureHandle) {
-        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
-        AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
-        SkASSERT(canvas);
-        SkASSERT(picture);
-        picture->draw(canvas);
-    }
-
-    static jboolean serialize(JNIEnv* env, jobject, jlong pictureHandle,
-                              jobject jstream, jbyteArray jstorage) {
-        AndroidPicture* picture = reinterpret_cast<AndroidPicture*>(pictureHandle);
-        SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
-
-        if (NULL != strm) {
-            picture->serialize(strm);
-            delete strm;
-            return JNI_TRUE;
-        }
-        return JNI_FALSE;
-    }
-
-    static jint getWidth(JNIEnv* env, jobject jpic) {
-        NPE_CHECK_RETURN_ZERO(env, jpic);
-        AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic);
-        int width = pict->width();
-        return static_cast<jint>(width);
-    }
-
-    static jint getHeight(JNIEnv* env, jobject jpic) {
-        NPE_CHECK_RETURN_ZERO(env, jpic);
-        AndroidPicture* pict = GraphicsJNI::getNativePicture(env, jpic);
-        int height = pict->height();
-        return static_cast<jint>(height);
-    }
-
-    static jlong beginRecording(JNIEnv* env, jobject, jlong pictHandle,
-                                jint w, jint h) {
-        AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle);
-        // beginRecording does not ref its return value, it just returns it.
-        SkCanvas* canvas = pict->beginRecording(w, h);
-        // the java side will wrap this guy in a Canvas.java, which will call
-        // unref in its finalizer, so we have to ref it here, so that both that
-        // Canvas.java and our picture can both be owners
-        canvas->ref();
-        return reinterpret_cast<jlong>(canvas);
-    }
-
-    static void endRecording(JNIEnv* env, jobject, jlong pictHandle) {
-        AndroidPicture* pict = reinterpret_cast<AndroidPicture*>(pictHandle);
-        pict->endRecording();
-    }
-};
-
-static JNINativeMethod gPictureMethods[] = {
-    {"getWidth", "()I", (void*) SkPictureGlue::getWidth},
-    {"getHeight", "()I", (void*) SkPictureGlue::getHeight},
-    {"nativeConstructor", "(J)J", (void*) SkPictureGlue::newPicture},
-    {"nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", (void*)SkPictureGlue::deserialize},
-    {"nativeBeginRecording", "(JII)J", (void*) SkPictureGlue::beginRecording},
-    {"nativeEndRecording", "(J)V", (void*) SkPictureGlue::endRecording},
-    {"nativeDraw", "(JJ)V", (void*) SkPictureGlue::draw},
-    {"nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", (void*)SkPictureGlue::serialize},
-    {"nativeDestructor","(J)V", (void*) SkPictureGlue::killPicture}
-};
-
-#include <android_runtime/AndroidRuntime.h>
-
-#define REG(env, name, array) \
-    result = android::AndroidRuntime::registerNativeMethods(env, name, array, \
-    SK_ARRAY_COUNT(array));  \
-    if (result < 0) return result
-
-int register_android_graphics_Picture(JNIEnv* env) {
-    int result;
-
-    REG(env, "android/graphics/Picture", gPictureMethods);
-
-    return result;
 }
 
+SkCanvas* Picture::beginRecording(int width, int height) {
+    mPicture.reset(NULL);
+    mRecorder.reset(new SkPictureRecorder);
+    mWidth = width;
+    mHeight = height;
+    return mRecorder->beginRecording(width, height, NULL, 0);
 }
 
+void Picture::endRecording() {
+    if (NULL != mRecorder.get()) {
+        mPicture.reset(mRecorder->endRecording());
+        mRecorder.reset(NULL);
+    }
+}
 
+int Picture::width() const {
+    if (NULL != mPicture.get()) {
+        SkASSERT(mPicture->width() == mWidth);
+        SkASSERT(mPicture->height() == mHeight);
+    }
+
+    return mWidth;
+}
+
+int Picture::height() const {
+    if (NULL != mPicture.get()) {
+        SkASSERT(mPicture->width() == mWidth);
+        SkASSERT(mPicture->height() == mHeight);
+    }
+
+    return mHeight;
+}
+
+Picture* Picture::CreateFromStream(SkStream* stream) {
+    Picture* newPict = new Picture;
+
+    newPict->mPicture.reset(SkPicture::CreateFromStream(stream));
+    if (NULL != newPict->mPicture.get()) {
+        newPict->mWidth = newPict->mPicture->width();
+        newPict->mHeight = newPict->mPicture->height();
+    }
+
+    return newPict;
+}
+
+void Picture::serialize(SkWStream* stream) const {
+    if (NULL != mRecorder.get()) {
+        SkAutoTDelete<SkPicture> tempPict(this->makePartialCopy());
+        tempPict->serialize(stream);
+    } else if (NULL != mPicture.get()) {
+        mPicture->serialize(stream);
+    } else {
+        SkPicture empty;
+        empty.serialize(stream);
+    }
+}
+
+void Picture::draw(SkCanvas* canvas) {
+    if (NULL != mRecorder.get()) {
+        this->endRecording();
+        SkASSERT(NULL != mPicture.get());
+    }
+    if (NULL != mPicture.get()) {
+        // TODO: remove this const_cast once pictures are immutable
+        const_cast<SkPicture*>(mPicture.get())->draw(canvas);
+    }
+}
+
+SkPicture* Picture::makePartialCopy() const {
+    SkASSERT(NULL != mRecorder.get());
+
+    SkPictureRecorder reRecorder;
+
+    SkCanvas* canvas = reRecorder.beginRecording(mWidth, mHeight, NULL, 0);
+    mRecorder->partialReplay(canvas);
+    return reRecorder.endRecording();
+}
+
+}; // namespace android
diff --git a/core/jni/android/graphics/AndroidPicture.h b/core/jni/android/graphics/Picture.h
similarity index 85%
rename from core/jni/android/graphics/AndroidPicture.h
rename to core/jni/android/graphics/Picture.h
index f434941..abb0403 100644
--- a/core/jni/android/graphics/AndroidPicture.h
+++ b/core/jni/android/graphics/Picture.h
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef ANDROID_PICTURE_H
-#define ANDROID_PICTURE_H
+#ifndef ANDROID_GRAPHICS_PICTURE_H
+#define ANDROID_GRAPHICS_PICTURE_H
 
 #include "SkPicture.h"
 #include "SkPictureRecorder.h"
@@ -28,13 +28,15 @@
 class SkStream;
 class SkWStream;
 
+namespace android {
+
 // Skia's SkPicture class has been split into an SkPictureRecorder
 // and an SkPicture. AndroidPicture recreates the functionality
 // of the old SkPicture interface by flip-flopping between the two
 // new classes.
-class AndroidPicture {
+class Picture {
 public:
-    explicit AndroidPicture(const AndroidPicture* src = NULL);
+    explicit Picture(const Picture* src = NULL);
 
     SkCanvas* beginRecording(int width, int height);
 
@@ -44,7 +46,7 @@
 
     int height() const;
 
-    static AndroidPicture* CreateFromStream(SkStream* stream);
+    static Picture* CreateFromStream(SkStream* stream);
 
     void serialize(SkWStream* stream) const;
 
@@ -60,4 +62,6 @@
     // resulting picture will have balanced saves and restores.
     SkPicture* makePartialCopy() const;
 };
-#endif // ANDROID_PICTURE_H
+
+}; // namespace android
+#endif // ANDROID_GRAPHICS_PICTURE_H
diff --git a/core/jni/android_graphics_Picture.cpp b/core/jni/android_graphics_Picture.cpp
new file mode 100644
index 0000000..f827907
--- /dev/null
+++ b/core/jni/android_graphics_Picture.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 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.
+ * 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.
+ */
+
+#include "jni.h"
+#include "GraphicsJNI.h"
+#include <android_runtime/AndroidRuntime.h>
+
+#include "Picture.h"
+
+#include "SkCanvas.h"
+#include "SkStream.h"
+#include "SkTemplates.h"
+#include "CreateJavaOutputStreamAdaptor.h"
+
+namespace android {
+
+static jlong android_graphics_Picture_newPicture(JNIEnv* env, jobject, jlong srcHandle) {
+    const Picture* src = reinterpret_cast<Picture*>(srcHandle);
+    return reinterpret_cast<jlong>(new Picture(src));
+}
+
+static jlong android_graphics_Picture_deserialize(JNIEnv* env, jobject, jobject jstream,
+                                                  jbyteArray jstorage) {
+    Picture* picture = NULL;
+    SkStream* strm = CreateJavaInputStreamAdaptor(env, jstream, jstorage);
+    if (strm) {
+        picture = Picture::CreateFromStream(strm);
+        delete strm;
+    }
+    return reinterpret_cast<jlong>(picture);
+}
+
+static void android_graphics_Picture_killPicture(JNIEnv* env, jobject, jlong pictureHandle) {
+    Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
+    SkASSERT(picture);
+    delete picture;
+}
+
+static void android_graphics_Picture_draw(JNIEnv* env, jobject, jlong canvasHandle,
+                                          jlong pictureHandle) {
+    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(canvasHandle);
+    Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
+    SkASSERT(canvas);
+    SkASSERT(picture);
+    picture->draw(canvas);
+}
+
+static jboolean android_graphics_Picture_serialize(JNIEnv* env, jobject, jlong pictureHandle,
+                                                   jobject jstream, jbyteArray jstorage) {
+    Picture* picture = reinterpret_cast<Picture*>(pictureHandle);
+    SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
+
+    if (NULL != strm) {
+        picture->serialize(strm);
+        delete strm;
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+
+static jint android_graphics_Picture_getWidth(JNIEnv* env, jobject, jlong pictureHandle) {
+    Picture* pict = reinterpret_cast<Picture*>(pictureHandle);
+    return static_cast<jint>(pict->width());
+}
+
+static jint android_graphics_Picture_getHeight(JNIEnv* env, jobject, jlong pictureHandle) {
+    Picture* pict = reinterpret_cast<Picture*>(pictureHandle);
+    return static_cast<jint>(pict->height());
+}
+
+static jlong android_graphics_Picture_beginRecording(JNIEnv* env, jobject, jlong pictHandle,
+                                                     jint w, jint h) {
+    Picture* pict = reinterpret_cast<Picture*>(pictHandle);
+    // beginRecording does not ref its return value, it just returns it.
+    SkCanvas* canvas = pict->beginRecording(w, h);
+    // the java side will wrap this guy in a Canvas.java, which will call
+    // unref in its finalizer, so we have to ref it here, so that both that
+    // Canvas.java and our picture can both be owners
+    canvas->ref();
+    return reinterpret_cast<jlong>(canvas);
+}
+
+static void android_graphics_Picture_endRecording(JNIEnv* env, jobject, jlong pictHandle) {
+    Picture* pict = reinterpret_cast<Picture*>(pictHandle);
+    pict->endRecording();
+}
+
+static JNINativeMethod gMethods[] = {
+    {"nativeGetWidth", "(J)I", (void*) android_graphics_Picture_getWidth},
+    {"nativeGetHeight", "(J)I", (void*) android_graphics_Picture_getHeight},
+    {"nativeConstructor", "(J)J", (void*) android_graphics_Picture_newPicture},
+    {"nativeCreateFromStream", "(Ljava/io/InputStream;[B)J", (void*)android_graphics_Picture_deserialize},
+    {"nativeBeginRecording", "(JII)J", (void*) android_graphics_Picture_beginRecording},
+    {"nativeEndRecording", "(J)V", (void*) android_graphics_Picture_endRecording},
+    {"nativeDraw", "(JJ)V", (void*) android_graphics_Picture_draw},
+    {"nativeWriteToStream", "(JLjava/io/OutputStream;[B)Z", (void*)android_graphics_Picture_serialize},
+    {"nativeDestructor","(J)V", (void*) android_graphics_Picture_killPicture}
+};
+
+int register_android_graphics_Picture(JNIEnv* env) {
+    return AndroidRuntime::registerNativeMethods(env, "android/graphics/Picture", gMethods, NELEM(gMethods));
+}
+
+}; // namespace android
diff --git a/core/jni/android_hardware_Camera.cpp b/core/jni/android_hardware_Camera.cpp
index 3a53331..f8bab24 100644
--- a/core/jni/android_hardware_Camera.cpp
+++ b/core/jni/android_hardware_Camera.cpp
@@ -36,6 +36,11 @@
 
 using namespace android;
 
+enum {
+    // Keep up to date with Camera.java
+    CAMERA_HAL_API_VERSION_NORMAL_CONNECT = -2,
+};
+
 struct fields_t {
     jfieldID    context;
     jfieldID    facing;
@@ -466,7 +471,7 @@
 
 // connect to camera service
 static jint android_hardware_Camera_native_setup(JNIEnv *env, jobject thiz,
-    jobject weak_this, jint cameraId, jstring clientPackageName)
+    jobject weak_this, jint cameraId, jint halVersion, jstring clientPackageName)
 {
     // Convert jstring to String16
     const char16_t *rawClientName = env->GetStringChars(clientPackageName, NULL);
@@ -474,8 +479,18 @@
     String16 clientName(rawClientName, rawClientNameLen);
     env->ReleaseStringChars(clientPackageName, rawClientName);
 
-    sp<Camera> camera = Camera::connect(cameraId, clientName,
-            Camera::USE_CALLING_UID);
+    sp<Camera> camera;
+    if (halVersion == CAMERA_HAL_API_VERSION_NORMAL_CONNECT) {
+        // Default path: hal version is don't care, do normal camera connect.
+        camera = Camera::connect(cameraId, clientName,
+                Camera::USE_CALLING_UID);
+    } else {
+        jint status = Camera::connectLegacy(cameraId, halVersion, clientName,
+                Camera::USE_CALLING_UID, camera);
+        if (status != NO_ERROR) {
+            return status;
+        }
+    }
 
     if (camera == NULL) {
         return -EACCES;
@@ -510,7 +525,6 @@
 // finalizer is invoked later.
 static void android_hardware_Camera_release(JNIEnv *env, jobject thiz)
 {
-    // TODO: Change to ALOGV
     ALOGV("release camera");
     JNICameraContext* context = NULL;
     sp<Camera> camera;
@@ -891,7 +905,7 @@
     "(ILandroid/hardware/Camera$CameraInfo;)V",
     (void*)android_hardware_Camera_getCameraInfo },
   { "native_setup",
-    "(Ljava/lang/Object;ILjava/lang/String;)I",
+    "(Ljava/lang/Object;IILjava/lang/String;)I",
     (void*)android_hardware_Camera_native_setup },
   { "native_release",
     "()V",
diff --git a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
index 40e9544..57058a6 100644
--- a/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
+++ b/core/jni/android_hardware_camera2_legacy_LegacyCameraDevice.cpp
@@ -15,6 +15,7 @@
  */
 
 #define LOG_TAG "Legacy-CameraDevice-JNI"
+// #define LOG_NDEBUG 0
 #include <utils/Log.h>
 #include <utils/Errors.h>
 #include <utils/Trace.h>
@@ -26,6 +27,7 @@
 
 #include <ui/GraphicBuffer.h>
 #include <system/window.h>
+#include <hardware/camera3.h>
 
 using namespace android;
 
@@ -35,6 +37,8 @@
 
 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(*(a)))
 
+#define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
+
 /**
  * Convert from RGB 888 to Y'CbCr using the conversion specified in ITU-R BT.601 for
  * digital RGB with K_b = 0.114, and K_r = 0.299.
@@ -116,8 +120,9 @@
         return err;
     }
 
-    ALOGV("%s: Setting buffer count to %d", __FUNCTION__,
-          maxBufferSlack + 1 + minUndequeuedBuffers);
+    ALOGV("%s: Setting buffer count to %d, size to (%dx%d), fmt (0x%x)", __FUNCTION__,
+          maxBufferSlack + 1 + minUndequeuedBuffers,
+          width, height, pixelFmt);
     err = native_window_set_buffer_count(anw.get(), maxBufferSlack + 1 + minUndequeuedBuffers);
     if (err != NO_ERROR) {
         ALOGE("%s: Failed to set native window buffer count, error %s (%d).", __FUNCTION__,
@@ -146,16 +151,41 @@
                              int32_t width, // Width of the pixelBuffer
                              int32_t height, // Height of the pixelBuffer
                              int32_t pixelFmt, // Format of the pixelBuffer
-                             int64_t bufSize) {
+                             int32_t bufSize) {
     ATRACE_CALL();
     status_t err = NO_ERROR;
     ANativeWindowBuffer* anb;
-    ALOGV("%s: Dequeue buffer from %p",__FUNCTION__, anw.get());
+    ALOGV("%s: Dequeue buffer from %p %dx%d (fmt=%x, size=%x)",
+            __FUNCTION__, anw.get(), width, height, pixelFmt, bufSize);
+
+    if (anw == 0) {
+        ALOGE("%s: anw must not be NULL", __FUNCTION__);
+        return BAD_VALUE;
+    } else if (pixelBuffer == NULL) {
+        ALOGE("%s: pixelBuffer must not be NULL", __FUNCTION__);
+        return BAD_VALUE;
+    } else if (width < 0) {
+        ALOGE("%s: width must be non-negative", __FUNCTION__);
+        return BAD_VALUE;
+    } else if (height < 0) {
+        ALOGE("%s: height must be non-negative", __FUNCTION__);
+        return BAD_VALUE;
+    } else if (bufSize < 0) {
+        ALOGE("%s: bufSize must be non-negative", __FUNCTION__);
+        return BAD_VALUE;
+    }
+
+    if (width < 0 || height < 0 || bufSize < 0) {
+        ALOGE("%s: Illegal argument, negative dimension passed to produceFrame", __FUNCTION__);
+        return BAD_VALUE;
+    }
 
     // TODO: Switch to using Surface::lock and Surface::unlockAndPost
     err = native_window_dequeue_buffer_and_wait(anw.get(), &anb);
     if (err != NO_ERROR) return err;
 
+    // TODO: check anb is large enough to store the results
+
     sp<GraphicBuffer> buf(new GraphicBuffer(anb, /*keepOwnership*/false));
 
     switch(pixelFmt) {
@@ -181,6 +211,41 @@
                     uPlane, vPlane, chromaStep, yStride, chromaStride);
             break;
         }
+        case HAL_PIXEL_FORMAT_YV12: {
+            if (bufSize < width * height * 4) {
+                ALOGE("%s: PixelBuffer size %lld to small for given dimensions", __FUNCTION__,
+                        bufSize);
+                return BAD_VALUE;
+            }
+
+            if ((width & 1) || (height & 1)) {
+                ALOGE("%s: Dimens %dx%d are not divisible by 2.", __FUNCTION__, width, height);
+                return BAD_VALUE;
+            }
+
+            uint8_t* img = NULL;
+            ALOGV("%s: Lock buffer from %p for write", __FUNCTION__, anw.get());
+            err = buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+            if (err != NO_ERROR) {
+                ALOGE("%s: Error %s (%d) while locking gralloc buffer for write.", __FUNCTION__,
+                        strerror(-err), err);
+                return err;
+            }
+
+            uint32_t stride = buf->getStride();
+            LOG_ALWAYS_FATAL_IF(stride % 16, "Stride is not 16 pixel aligned %d", stride);
+
+            uint32_t cStride = ALIGN(stride / 2, 16);
+            size_t chromaStep = 1;
+
+            uint8_t* yPlane = img;
+            uint8_t* crPlane = img + static_cast<uint32_t>(height) * stride;
+            uint8_t* cbPlane = crPlane + cStride * static_cast<uint32_t>(height) / 2;
+
+            rgbToYuv420(pixelBuffer, width, height, yPlane,
+                    crPlane, cbPlane, chromaStep, stride, cStride);
+            break;
+        }
         case HAL_PIXEL_FORMAT_YCbCr_420_888: {
             // Software writes with YCbCr_420_888 format are unsupported
             // by the gralloc module for now
@@ -215,7 +280,12 @@
                         err);
                 return err;
             }
+            struct camera3_jpeg_blob footer = {
+                jpeg_blob_id: CAMERA3_JPEG_BLOB_ID,
+                jpeg_size: (uint32_t)width
+            };
             memcpy(img, pixelBuffer, width);
+            memcpy(img + anb->width - sizeof(footer), &footer, sizeof(footer));
             break;
         }
         default: {
diff --git a/core/jni/android_media_AudioFormat.h b/core/jni/android_media_AudioFormat.h
index a2b1ed9..807dd32 100644
--- a/core/jni/android_media_AudioFormat.h
+++ b/core/jni/android_media_AudioFormat.h
@@ -20,11 +20,15 @@
 #include <system/audio.h>
 
 // keep these values in sync with AudioFormat.java
-#define ENCODING_PCM_16BIT 2
-#define ENCODING_PCM_8BIT  3
-#define ENCODING_PCM_FLOAT 4
-#define ENCODING_INVALID 0
-#define ENCODING_DEFAULT 1
+#define ENCODING_PCM_16BIT  2
+#define ENCODING_PCM_8BIT   3
+#define ENCODING_PCM_FLOAT  4
+#define ENCODING_AC3        5
+#define ENCODING_E_AC3      6
+#define ENCODING_INVALID    0
+#define ENCODING_DEFAULT    1
+
+
 
 #define CHANNEL_INVALID 0
 #define CHANNEL_OUT_DEFAULT 1
@@ -38,6 +42,10 @@
         return AUDIO_FORMAT_PCM_8_BIT;
     case ENCODING_PCM_FLOAT:
         return AUDIO_FORMAT_PCM_FLOAT;
+    case ENCODING_AC3:
+        return AUDIO_FORMAT_AC3;
+    case ENCODING_E_AC3:
+        return AUDIO_FORMAT_E_AC3;
     case ENCODING_DEFAULT:
         return AUDIO_FORMAT_DEFAULT;
     default:
@@ -54,6 +62,10 @@
         return ENCODING_PCM_8BIT;
     case AUDIO_FORMAT_PCM_FLOAT:
         return ENCODING_PCM_FLOAT;
+    case AUDIO_FORMAT_AC3:
+        return ENCODING_AC3;
+    case AUDIO_FORMAT_E_AC3:
+        return ENCODING_E_AC3;
     case AUDIO_FORMAT_DEFAULT:
         return ENCODING_DEFAULT;
     default:
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index ee4c619..849531c 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -287,6 +287,8 @@
     env->CallStaticVoidMethod(clazz, env->GetStaticMethodID(clazz,
                               "errorCallbackFromNative","(I)V"),
                               check_AudioSystem_Command(err));
+
+    env->DeleteLocalRef(clazz);
 }
 
 static jint
diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp
index e548e91..264a9ae 100644
--- a/core/jni/android_media_AudioTrack.cpp
+++ b/core/jni/android_media_AudioTrack.cpp
@@ -220,8 +220,13 @@
     }
 
     // compute the frame count
-    const size_t bytesPerSample = audio_bytes_per_sample(format);
-    size_t frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
+    size_t frameCount;
+    if (audio_is_linear_pcm(format)) {
+        const size_t bytesPerSample = audio_bytes_per_sample(format);
+        frameCount = buffSizeInBytes / (channelCount * bytesPerSample);
+    } else {
+        frameCount = buffSizeInBytes;
+    }
 
     jclass clazz = env->GetObjectClass(thiz);
     if (clazz == NULL) {
@@ -266,7 +271,7 @@
             format,// word length, PCM
             nativeChannelMask,
             frameCount,
-            AUDIO_OUTPUT_FLAG_NONE,
+            audio_is_linear_pcm(format) ? AUDIO_OUTPUT_FLAG_NONE : AUDIO_OUTPUT_FLAG_DIRECT,
             audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
             0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
             0,// shared mem
@@ -478,14 +483,6 @@
         switch (format) {
 
         default:
-            // TODO Currently the only possible values for format are AUDIO_FORMAT_PCM_16_BIT,
-            // AUDIO_FORMAT_PCM_8_BIT, and AUDIO_FORMAT_PCM_FLOAT,
-            // due to the limited set of values for audioFormat.
-            // The next section of the switch will probably work for more formats, but it has only
-            // been tested for AUDIO_FORMAT_PCM_16_BIT and AUDIO_FORMAT_PCM_FLOAT,
-            // so that's why the "default" case fails.
-            break;
-
         case AUDIO_FORMAT_PCM_FLOAT:
         case AUDIO_FORMAT_PCM_16_BIT: {
             // writing to shared memory, check for capacity
@@ -904,8 +901,12 @@
         return -1;
     }
     const audio_format_t format = audioFormatToNative(audioFormat);
-    const size_t bytesPerSample = audio_bytes_per_sample(format);
-    return frameCount * channelCount * bytesPerSample;
+    if (audio_is_linear_pcm(format)) {
+        const size_t bytesPerSample = audio_bytes_per_sample(format);
+        return frameCount * channelCount * bytesPerSample;
+    } else {
+        return frameCount;
+    }
 }
 
 // ----------------------------------------------------------------------------
diff --git a/core/jni/android_os_Debug.cpp b/core/jni/android_os_Debug.cpp
index 86207f0..87ee618 100644
--- a/core/jni/android_os_Debug.cpp
+++ b/core/jni/android_os_Debug.cpp
@@ -174,21 +174,21 @@
 
     ssize_t pss = memtrack_proc_graphics_pss(p);
     if (pss < 0) {
-        ALOGW("failed to get graphics pss: %d", pss);
+        ALOGW("failed to get graphics pss: %zd", pss);
         return pss;
     }
     graphics_mem->graphics = pss / 1024;
 
     pss = memtrack_proc_gl_pss(p);
     if (pss < 0) {
-        ALOGW("failed to get gl pss: %d", pss);
+        ALOGW("failed to get gl pss: %zd", pss);
         return pss;
     }
     graphics_mem->gl = pss / 1024;
 
     pss = memtrack_proc_other_pss(p);
     if (pss < 0) {
-        ALOGW("failed to get other pss: %d", pss);
+        ALOGW("failed to get other pss: %zd", pss);
         return pss;
     }
     graphics_mem->other = pss / 1024;
@@ -231,9 +231,9 @@
     unsigned referenced = 0;
     unsigned temp;
 
-    unsigned long int start;
-    unsigned long int end = 0;
-    unsigned long int prevEnd = 0;
+    uint64_t start;
+    uint64_t end = 0;
+    uint64_t prevEnd = 0;
     char* name;
     int name_pos;
 
@@ -255,7 +255,7 @@
         if (len < 1) return;
         line[--len] = 0;
 
-        if (sscanf(line, "%lx-%lx %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
+        if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d%n", &start, &end, &name_pos) != 2) {
             skip = true;
         } else {
             while (isspace(line[name_pos])) {
@@ -371,7 +371,7 @@
                 referenced = temp;
             } else if (line[0] == 'S' && sscanf(line, "Swap: %d kB", &temp) == 1) {
                 swapped_out = temp;
-            } else if (strlen(line) > 30 && line[8] == '-' && line[17] == ' ') {
+            } else if (sscanf(line, "%" SCNx64 "-%" SCNx64 " %*s %*x %*x:%*x %*d", &start, &end) == 2) {
                 // looks like a new mapping
                 // example: "10000000-10001000 ---p 10000000 00:00 0"
                 break;
diff --git a/core/jni/android_server_FingerprintManager.cpp b/core/jni/android_server_FingerprintManager.cpp
index ad36843c..b174d1b 100644
--- a/core/jni/android_server_FingerprintManager.cpp
+++ b/core/jni/android_server_FingerprintManager.cpp
@@ -57,21 +57,11 @@
     fingerprint_device_t *device;
 } gContext;
 
-// TODO: remove after driver update to use new HAL
-fingerprint_msg_type_t hackTilFpDriverUpdate(fingerprint_msg_type_t t) {
-    switch(static_cast<int>(t)) {
-        case 1: return FINGERPRINT_PROCESSED;
-        case 2: return FINGERPRINT_TEMPLATE_ENROLLING;
-        default: return t;
-    }
-}
-
 // Called by the HAL to notify us of fingerprint events
 static void hal_notify_callback(fingerprint_msg_t msg) {
     uint32_t arg1 = 0;
     uint32_t arg2 = 0;
     uint32_t arg3 = 0; // TODO
-    msg.type = hackTilFpDriverUpdate(msg.type);
     switch (msg.type) {
         case FINGERPRINT_ERROR:
             arg1 = msg.data.error;
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 01f4d3a..a6b65cc 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -45,6 +45,8 @@
 #define POLICY_DEBUG 0
 #define GUARD_THREAD_PRIORITY 0
 
+#define DEBUG_PROC(x) //x
+
 using namespace android;
 
 #if GUARD_THREAD_PRIORITY
@@ -725,6 +727,7 @@
         const char term = (char)(mode&PROC_TERM_MASK);
         const jsize start = i;
         if (i >= endIndex) {
+            DEBUG_PROC(ALOGW("Ran off end of data @%d", i));
             res = JNI_FALSE;
             break;
         }
@@ -822,19 +825,20 @@
         return JNI_FALSE;
     }
     int fd = open(file8, O_RDONLY);
-    env->ReleaseStringUTFChars(file, file8);
 
     if (fd < 0) {
-        //ALOGW("Unable to open process file: %s\n", file8);
+        DEBUG_PROC(ALOGW("Unable to open process file: %s\n", file8));
+        env->ReleaseStringUTFChars(file, file8);
         return JNI_FALSE;
     }
+    env->ReleaseStringUTFChars(file, file8);
 
     char buffer[256];
     const int len = read(fd, buffer, sizeof(buffer)-1);
     close(fd);
 
     if (len < 0) {
-        //ALOGW("Unable to open process file: %s fd=%d\n", file8, fd);
+        DEBUG_PROC(ALOGW("Unable to open process file: %s fd=%d\n", file8, fd));
         return JNI_FALSE;
     }
     buffer[len] = 0;
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 4a6e117..2a1fd1b 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -20,15 +20,10 @@
 #include "GraphicsJNI.h"
 #include <nativehelper/JNIHelp.h>
 
-#include "android_view_GraphicBuffer.h"
-
 #include <android_runtime/AndroidRuntime.h>
-#include <android_runtime/android_graphics_SurfaceTexture.h>
 
 #include <androidfw/ResourceTypes.h>
 
-#include <gui/GLConsumer.h>
-
 #include <private/hwui/DrawGlInfo.h>
 
 #include <cutils/properties.h>
@@ -37,15 +32,13 @@
 #include <SkCanvas.h>
 #include <SkMatrix.h>
 #include <SkPaint.h>
+#include <SkPorterDuff.h>
 #include <SkRegion.h>
 #include <SkScalerContext.h>
 #include <SkTemplates.h>
 #include <SkXfermode.h>
 
 #include <DisplayListRenderer.h>
-#include <LayerRenderer.h>
-#include <OpenGLRenderer.h>
-#include <Stencil.h>
 #include <Rect.h>
 #include <RenderNode.h>
 #include <CanvasProperty.h>
@@ -64,7 +57,7 @@
 using namespace uirenderer;
 
 /**
- * Note: OpenGLRenderer JNI layer is generated and compiled only on supported
+ * Note: DisplayListRenderer JNI layer is generated and compiled only on supported
  *       devices. This means all the logic must be compiled only when the
  *       preprocessor variable USE_OPENGL_RENDERER is defined.
  */
@@ -96,8 +89,8 @@
 
 static void android_view_GLES20Canvas_destroyRenderer(JNIEnv* env, jobject clazz,
         jlong rendererPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    RENDERER_LOGD("Destroy OpenGLRenderer");
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
+    RENDERER_LOGD("Destroy DisplayListRenderer");
     delete renderer;
 }
 
@@ -107,26 +100,26 @@
 
 static void android_view_GLES20Canvas_setViewport(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jint width, jint height) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->setViewport(width, height);
 }
 
 static int android_view_GLES20Canvas_prepare(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jboolean opaque) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     return renderer->prepare(opaque);
 }
 
 static int android_view_GLES20Canvas_prepareDirty(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jint left, jint top, jint right, jint bottom,
         jboolean opaque) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     return renderer->prepareDirty(left, top, right, bottom, opaque);
 }
 
 static void android_view_GLES20Canvas_finish(JNIEnv* env, jobject clazz,
         jlong rendererPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->finish();
 }
 
@@ -154,7 +147,7 @@
 
 static jint android_view_GLES20Canvas_callDrawGLFunction(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong functorPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     Functor* functor = reinterpret_cast<Functor*>(functorPtr);
     android::uirenderer::Rect dirty;
     return renderer->callDrawGLFunction(functor, dirty);
@@ -178,25 +171,25 @@
 
 static jint android_view_GLES20Canvas_save(JNIEnv* env, jobject clazz, jlong rendererPtr,
         jint flags) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     return renderer->save(flags);
 }
 
 static jint android_view_GLES20Canvas_getSaveCount(JNIEnv* env, jobject clazz,
         jlong rendererPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     return renderer->getSaveCount();
 }
 
 static void android_view_GLES20Canvas_restore(JNIEnv* env, jobject clazz,
         jlong rendererPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->restore();
 }
 
 static void android_view_GLES20Canvas_restoreToCount(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jint saveCount) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->restoreToCount(saveCount);
 }
 
@@ -207,14 +200,14 @@
 static jint android_view_GLES20Canvas_saveLayer(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
         jlong paintPtr, jint saveFlags) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     return renderer->saveLayer(left, top, right, bottom, paint, saveFlags);
 }
 
 static jint android_view_GLES20Canvas_saveLayerClip(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong paintPtr, jint saveFlags) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     const android::uirenderer::Rect& bounds(renderer->getLocalClipBounds());
     return renderer->saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom,
@@ -224,13 +217,13 @@
 static jint android_view_GLES20Canvas_saveLayerAlpha(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
         jint alpha, jint saveFlags) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     return renderer->saveLayerAlpha(left, top, right, bottom, alpha, saveFlags);
 }
 
 static jint android_view_GLES20Canvas_saveLayerAlphaClip(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jint alpha, jint saveFlags) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     const android::uirenderer::Rect& bounds(renderer->getLocalClipBounds());
     return renderer->saveLayerAlpha(bounds.left, bounds.top, bounds.right, bounds.bottom,
             alpha, saveFlags);
@@ -242,7 +235,7 @@
 
 static jboolean android_view_GLES20Canvas_quickReject(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     const bool result = renderer->quickRejectConservative(left, top, right, bottom);
     return result ? JNI_TRUE : JNI_FALSE;
 }
@@ -250,7 +243,7 @@
 static jboolean android_view_GLES20Canvas_clipRectF(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
         jint op) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     const bool result = renderer->clipRect(left, top, right, bottom,
                                            static_cast<SkRegion::Op>(op));
     return result ? JNI_TRUE : JNI_FALSE;
@@ -259,7 +252,7 @@
 static jboolean android_view_GLES20Canvas_clipRect(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jint left, jint top, jint right, jint bottom,
         jint op) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     const bool result = renderer->clipRect(float(left), float(top), float(right),
                                            float(bottom),
                                            static_cast<SkRegion::Op>(op));
@@ -268,7 +261,7 @@
 
 static jboolean android_view_GLES20Canvas_clipPath(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong pathPtr, jint op) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
     const bool result = renderer->clipPath(path, static_cast<SkRegion::Op>(op));
     return result ? JNI_TRUE : JNI_FALSE;
@@ -276,7 +269,7 @@
 
 static jboolean android_view_GLES20Canvas_clipRegion(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong regionPtr, jint op) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkRegion* region = reinterpret_cast<SkRegion*>(regionPtr);
     const bool result = renderer->clipRegion(region, static_cast<SkRegion::Op>(op));
     return result ? JNI_TRUE : JNI_FALSE;
@@ -284,7 +277,7 @@
 
 static jboolean android_view_GLES20Canvas_getClipBounds(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jobject rect) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     const android::uirenderer::Rect& bounds(renderer->getLocalClipBounds());
 
     env->CallVoidMethod(rect, gRectClassInfo.set,
@@ -299,45 +292,45 @@
 
 static void android_view_GLES20Canvas_translate(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat dx, jfloat dy) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->translate(dx, dy);
 }
 
 static void android_view_GLES20Canvas_rotate(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat degrees) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->rotate(degrees);
 }
 
 static void android_view_GLES20Canvas_scale(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat sx, jfloat sy) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->scale(sx, sy);
 }
 
 static void android_view_GLES20Canvas_skew(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat sx, jfloat sy) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->skew(sx, sy);
 }
 
 static void android_view_GLES20Canvas_setMatrix(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong matrixPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
     renderer->setMatrix(matrix ? *matrix : SkMatrix::I());
 }
 
 static void android_view_GLES20Canvas_getMatrix(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong matrixPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
     renderer->getMatrix(matrix);
 }
 
 static void android_view_GLES20Canvas_concatMatrix(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong matrixPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
     renderer->concatMatrix(*matrix);
 }
@@ -353,7 +346,7 @@
     // This object allows the renderer to allocate a global JNI ref to the buffer object.
     JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
 
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawBitmap(bitmap, left, top, paint);
 }
@@ -366,7 +359,7 @@
     // This object allows the renderer to allocate a global JNI ref to the buffer object.
     JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
 
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
             dstLeft, dstTop, dstRight, dstBottom, paint);
@@ -379,7 +372,7 @@
     // This object allows the renderer to allocate a global JNI ref to the buffer object.
     JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
 
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawBitmap(bitmap, *matrix, paint);
@@ -388,11 +381,11 @@
 static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jintArray colors, jint offset, jint stride,
         jfloat left, jfloat top, jint width, jint height, jboolean hasAlpha, jlong paintPtr) {
+    const SkImageInfo info = SkImageInfo::Make(width, height,
+                               hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
+                               kPremul_SkAlphaType);
     SkBitmap* bitmap = new SkBitmap;
-    bitmap->setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config : SkBitmap::kRGB_565_Config,
-            width, height);
-
-    if (!bitmap->allocPixels()) {
+    if (!bitmap->allocPixels(info)) {
         delete bitmap;
         return;
     }
@@ -402,7 +395,7 @@
         return;
     }
 
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawBitmapData(bitmap, left, top, paint);
 
@@ -423,7 +416,7 @@
     jfloat* verticesArray = vertices ? env->GetFloatArrayElements(vertices, NULL) + offset : NULL;
     jint* colorsArray = colors ? env->GetIntArrayElements(colors, NULL) + colorOffset : NULL;
 
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawBitmapMesh(bitmap, meshWidth, meshHeight, verticesArray, colorsArray, paint);
 
@@ -438,22 +431,23 @@
     // This object allows the renderer to allocate a global JNI ref to the buffer object.
     JavaHeapBitmapRef bitmapRef(env, bitmap, buffer);
 
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     Res_png_9patch* patch = reinterpret_cast<Res_png_9patch*>(patchPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawPatch(bitmap, patch, left, top, right, bottom, paint);
 }
 
 static void android_view_GLES20Canvas_drawColor(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jint color, jint mode) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    renderer->drawColor(color, static_cast<SkXfermode::Mode>(mode));
+        jlong rendererPtr, jint color, jint modeHandle) {
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
+    SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
+    renderer->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
 }
 
 static void android_view_GLES20Canvas_drawRect(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
         jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawRect(left, top, right, bottom, paint);
 }
@@ -461,21 +455,21 @@
 static void android_view_GLES20Canvas_drawRoundRect(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
         jfloat rx, jfloat ry, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawRoundRect(left, top, right, bottom, rx, ry, paint);
 }
 
 static void android_view_GLES20Canvas_drawCircle(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat x, jfloat y, jfloat radius, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawCircle(x, y, radius, paint);
 }
 
 static void android_view_GLES20Canvas_drawCircleProps(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong xPropPtr, jlong yPropPtr, jlong radiusPropPtr, jlong paintPropPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     CanvasPropertyPrimitive* xProp = reinterpret_cast<CanvasPropertyPrimitive*>(xPropPtr);
     CanvasPropertyPrimitive* yProp = reinterpret_cast<CanvasPropertyPrimitive*>(yPropPtr);
     CanvasPropertyPrimitive* radiusProp = reinterpret_cast<CanvasPropertyPrimitive*>(radiusPropPtr);
@@ -486,7 +480,7 @@
 static void android_view_GLES20Canvas_drawOval(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
         jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawOval(left, top, right, bottom, paint);
 }
@@ -494,14 +488,14 @@
 static void android_view_GLES20Canvas_drawArc(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloat left, jfloat top, jfloat right, jfloat bottom,
         jfloat startAngle, jfloat sweepAngle, jboolean useCenter, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawArc(left, top, right, bottom, startAngle, sweepAngle, useCenter, paint);
 }
 
 static void android_view_GLES20Canvas_drawRegionAsRects(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong regionPtr, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkRegion* region = reinterpret_cast<SkRegion*>(regionPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     if (paint->getStyle() != SkPaint::kFill_Style ||
@@ -531,7 +525,7 @@
 
 static void android_view_GLES20Canvas_drawPoints(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloatArray points, jint offset, jint count, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     jfloat* storage = env->GetFloatArrayElements(points, NULL);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawPoints(storage + offset, count, paint);
@@ -540,7 +534,7 @@
 
 static void android_view_GLES20Canvas_drawPath(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong pathPtr, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawPath(path, paint);
@@ -548,7 +542,7 @@
 
 static void android_view_GLES20Canvas_drawLines(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jfloatArray points, jint offset, jint count, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     jfloat* storage = env->GetFloatArrayElements(points, NULL);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     renderer->drawLines(storage + offset, count, paint);
@@ -561,13 +555,13 @@
 
 static void android_view_GLES20Canvas_setupPaintFilter(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jint clearBits, jint setBits) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->setupPaintFilter(clearBits, setBits);
 }
 
 static void android_view_GLES20Canvas_resetPaintFilter(JNIEnv* env, jobject clazz,
         jlong rendererPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     renderer->resetPaintFilter();
 }
 
@@ -594,7 +588,7 @@
 
 class RenderTextFunctor {
 public:
-    RenderTextFunctor(const Layout& layout, OpenGLRenderer* renderer, jfloat x, jfloat y,
+    RenderTextFunctor(const Layout& layout, DisplayListRenderer* renderer, jfloat x, jfloat y,
                 SkPaint* paint, uint16_t* glyphs, float* pos, float totalAdvance,
                 uirenderer::Rect& bounds)
             : layout(layout), renderer(renderer), x(x), y(y), paint(paint), glyphs(glyphs),
@@ -612,7 +606,7 @@
     }
 private:
     const Layout& layout;
-    OpenGLRenderer* renderer;
+    DisplayListRenderer* renderer;
     jfloat x;
     jfloat y;
     SkPaint* paint;
@@ -622,7 +616,7 @@
     uirenderer::Rect& bounds;
 };
 
-static void renderTextLayout(OpenGLRenderer* renderer, Layout* layout,
+static void renderTextLayout(DisplayListRenderer* renderer, Layout* layout,
     jfloat x, jfloat y, SkPaint* paint) {
     size_t nGlyphs = layout->nGlyphs();
     float* pos = new float[nGlyphs * 2];
@@ -640,7 +634,7 @@
 }
 #endif
 
-static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
+static void renderText(DisplayListRenderer* renderer, const jchar* text, int count,
         jfloat x, jfloat y, int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
 #ifdef USE_MINIKIN
     Layout layout;
@@ -669,8 +663,48 @@
 #endif
 }
 
-static void renderTextOnPath(OpenGLRenderer* renderer, const jchar* text, int count,
-        SkPath* path, jfloat hOffset, jfloat vOffset, int bidiFlags, SkPaint* paint) {
+#ifdef USE_MINIKIN
+class RenderTextOnPathFunctor {
+public:
+    RenderTextOnPathFunctor(const Layout& layout, DisplayListRenderer* renderer, float hOffset,
+                float vOffset, SkPaint* paint, SkPath* path)
+            : layout(layout), renderer(renderer), hOffset(hOffset), vOffset(vOffset),
+                paint(paint), path(path) {
+    }
+    void operator()(size_t start, size_t end) {
+        uint16_t glyphs[1];
+        for (size_t i = start; i < end; i++) {
+            glyphs[0] = layout.getGlyphId(i);
+            float x = hOffset + layout.getX(i);
+            float y = vOffset + layout.getY(i);
+            renderer->drawTextOnPath((const char*) glyphs, sizeof(glyphs), 1, path, x, y, paint);
+        }
+    }
+private:
+    const Layout& layout;
+    DisplayListRenderer* renderer;
+    float hOffset;
+    float vOffset;
+    SkPaint* paint;
+    SkPath* path;
+};
+#endif
+
+static void renderTextOnPath(DisplayListRenderer* renderer, const jchar* text, int count,
+        SkPath* path, jfloat hOffset, jfloat vOffset, int bidiFlags, SkPaint* paint,
+        TypefaceImpl* typeface) {
+#ifdef USE_MINIKIN
+    Layout layout;
+    std::string css = MinikinUtils::setLayoutProperties(&layout, paint, bidiFlags, typeface);
+    layout.doLayout(text, 0, count, count, css);
+    hOffset += MinikinUtils::hOffsetForTextAlign(paint, layout, *path);
+    SkPaint::Align align = paint->getTextAlign();
+    paint->setTextAlign(SkPaint::kLeft_Align);
+
+    RenderTextOnPathFunctor f(layout, renderer, hOffset, vOffset, paint, path);
+    MinikinUtils::forFontRun(layout, paint, f);
+    paint->setTextAlign(align);
+#else
     sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
             text, 0, count, count, bidiFlags);
     if (value == NULL) {
@@ -681,9 +715,10 @@
     int bytesCount = glyphsCount * sizeof(jchar);
     renderer->drawTextOnPath((const char*) glyphs, bytesCount, glyphsCount, path,
             hOffset, vOffset, paint);
+#endif
 }
 
-static void renderTextRun(OpenGLRenderer* renderer, const jchar* text,
+static void renderTextRun(DisplayListRenderer* renderer, const jchar* text,
         jint start, jint count, jint contextCount, jfloat x, jfloat y,
         int bidiFlags, SkPaint* paint, TypefaceImpl* typeface) {
 #ifdef USE_MINIKIN
@@ -716,7 +751,7 @@
 static void android_view_GLES20Canvas_drawTextArray(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jcharArray text, jint index, jint count,
         jfloat x, jfloat y, jint bidiFlags, jlong paintPtr, jlong typefacePtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     jchar* textArray = env->GetCharArrayElements(text, NULL);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
@@ -728,7 +763,7 @@
 static void android_view_GLES20Canvas_drawText(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jstring text, jint start, jint end,
         jfloat x, jfloat y, jint bidiFlags, jlong paintPtr, jlong typefacePtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     const jchar* textArray = env->GetStringChars(text, NULL);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
@@ -739,27 +774,31 @@
 
 static void android_view_GLES20Canvas_drawTextArrayOnPath(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jcharArray text, jint index, jint count,
-        jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+        jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr,
+        jlong typefacePtr) {
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     jchar* textArray = env->GetCharArrayElements(text, NULL);
     SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
 
     renderTextOnPath(renderer, textArray + index, count, path,
-            hOffset, vOffset, bidiFlags, paint);
+            hOffset, vOffset, bidiFlags, paint, typeface);
     env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
 }
 
 static void android_view_GLES20Canvas_drawTextOnPath(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jstring text, jint start, jint end,
-        jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+        jlong pathPtr, jfloat hOffset, jfloat vOffset, jint bidiFlags, jlong paintPtr,
+        jlong typefacePtr) {
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     const jchar* textArray = env->GetStringChars(text, NULL);
     SkPath* path = reinterpret_cast<SkPath*>(pathPtr);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
+    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
 
     renderTextOnPath(renderer, textArray + start, end - start, path,
-            hOffset, vOffset, bidiFlags, paint);
+            hOffset, vOffset, bidiFlags, paint, typeface);
     env->ReleaseStringChars(text, textArray);
 }
 
@@ -767,7 +806,7 @@
         jlong rendererPtr, jcharArray text, jint index, jint count,
         jint contextIndex, jint contextCount, jfloat x, jfloat y, jboolean isRtl,
         jlong paintPtr, jlong typefacePtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     jchar* textArray = env->GetCharArrayElements(text, NULL);
     SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
     TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefacePtr);
@@ -782,7 +821,7 @@
         jlong rendererPtr, jstring text, jint start, jint end,
         jint contextStart, int contextEnd, jfloat x, jfloat y, jboolean isRtl,
         jlong paintPtr, jlong typefacePtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     const jchar* textArray = env->GetStringChars(text, NULL);
     jint count = end - start;
     jint contextCount = contextEnd - contextStart;
@@ -795,49 +834,6 @@
     env->ReleaseStringChars(text, textArray);
 }
 
-static void renderPosText(OpenGLRenderer* renderer, const jchar* text, int count,
-        const jfloat* positions, jint bidiFlags, SkPaint* paint) {
-    sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
-            text, 0, count, count, bidiFlags);
-    if (value == NULL) {
-        return;
-    }
-    const jchar* glyphs = value->getGlyphs();
-    size_t glyphsCount = value->getGlyphsCount();
-    if (count < int(glyphsCount)) glyphsCount = count;
-    int bytesCount = glyphsCount * sizeof(jchar);
-
-    renderer->drawPosText((const char*) glyphs, bytesCount, glyphsCount, positions, paint);
-}
-
-static void android_view_GLES20Canvas_drawPosTextArray(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jcharArray text, jint index, jint count,
-        jfloatArray pos, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    jchar* textArray = env->GetCharArrayElements(text, NULL);
-    jfloat* positions = env->GetFloatArrayElements(pos, NULL);
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
-
-    renderPosText(renderer, textArray + index, count, positions, kBidi_LTR, paint);
-
-    env->ReleaseFloatArrayElements(pos, positions, JNI_ABORT);
-    env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
-}
-
-static void android_view_GLES20Canvas_drawPosText(JNIEnv* env, jobject clazz,
-        jlong rendererPtr, jstring text, jint start, jint end,
-        jfloatArray pos, jlong paintPtr) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    const jchar* textArray = env->GetStringChars(text, NULL);
-    jfloat* positions = env->GetFloatArrayElements(pos, NULL);
-    SkPaint* paint = reinterpret_cast<SkPaint*>(paintPtr);
-
-    renderPosText(renderer, textArray + start, end - start, positions, kBidi_LTR, paint);
-
-    env->ReleaseFloatArrayElements(pos, positions, JNI_ABORT);
-    env->ReleaseStringChars(text, textArray);
-}
-
 // ----------------------------------------------------------------------------
 // Display lists
 // ----------------------------------------------------------------------------
@@ -852,13 +848,13 @@
     return reinterpret_cast<jlong>(new DisplayListRenderer);
 }
 
-static jint android_view_GLES20Canvas_drawDisplayList(JNIEnv* env,
-        jobject clazz, jlong rendererPtr, jlong displayListPtr,
+static jint android_view_GLES20Canvas_drawRenderNode(JNIEnv* env,
+        jobject clazz, jlong rendererPtr, jlong renderNodePtr,
         jobject dirty, jint flags) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
-    RenderNode* displayList = reinterpret_cast<RenderNode*>(displayListPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
+    RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr);
     android::uirenderer::Rect bounds;
-    status_t status = renderer->drawDisplayList(displayList, bounds, flags);
+    status_t status = renderer->drawRenderNode(renderNode, bounds, flags);
     if (status != DrawGlInfo::kStatusDone && dirty != NULL) {
         env->CallVoidMethod(dirty, gRectClassInfo.set,
                 int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom));
@@ -872,7 +868,7 @@
 
 static void android_view_GLES20Canvas_drawLayer(JNIEnv* env, jobject clazz,
         jlong rendererPtr, jlong layerPtr, jfloat x, jfloat y) {
-    OpenGLRenderer* renderer = reinterpret_cast<OpenGLRenderer*>(rendererPtr);
+    DisplayListRenderer* renderer = reinterpret_cast<DisplayListRenderer*>(rendererPtr);
     Layer* layer = reinterpret_cast<Layer*>(layerPtr);
     renderer->drawLayer(layer, x, y);
 }
@@ -986,24 +982,18 @@
     { "nDrawText",          "(JLjava/lang/String;IIFFIJJ)V",
             (void*) android_view_GLES20Canvas_drawText },
 
-    { "nDrawTextOnPath",    "(J[CIIJFFIJ)V",   (void*) android_view_GLES20Canvas_drawTextArrayOnPath },
-    { "nDrawTextOnPath",    "(JLjava/lang/String;IIJFFIJ)V",
+    { "nDrawTextOnPath",    "(J[CIIJFFIJJ)V",  (void*) android_view_GLES20Canvas_drawTextArrayOnPath },
+    { "nDrawTextOnPath",    "(JLjava/lang/String;IIJFFIJJ)V",
             (void*) android_view_GLES20Canvas_drawTextOnPath },
 
     { "nDrawTextRun",       "(J[CIIIIFFZJJ)V",  (void*) android_view_GLES20Canvas_drawTextRunArray },
     { "nDrawTextRun",       "(JLjava/lang/String;IIIIFFZJJ)V",
             (void*) android_view_GLES20Canvas_drawTextRun },
 
-    { "nDrawPosText",       "(J[CII[FJ)V",     (void*) android_view_GLES20Canvas_drawPosTextArray },
-    { "nDrawPosText",       "(JLjava/lang/String;II[FJ)V",
-            (void*) android_view_GLES20Canvas_drawPosText },
+    { "nGetClipBounds",     "(JLandroid/graphics/Rect;)Z", (void*) android_view_GLES20Canvas_getClipBounds },
 
-    { "nGetClipBounds",     "(JLandroid/graphics/Rect;)Z",
-            (void*) android_view_GLES20Canvas_getClipBounds },
-
-    { "nFinishRecording",        "(J)J",      (void*) android_view_GLES20Canvas_finishRecording },
-    { "nDrawDisplayList",        "(JJLandroid/graphics/Rect;I)I",
-            (void*) android_view_GLES20Canvas_drawDisplayList },
+    { "nFinishRecording",   "(J)J",      (void*) android_view_GLES20Canvas_finishRecording },
+    { "nDrawRenderNode",    "(JJLandroid/graphics/Rect;I)I", (void*) android_view_GLES20Canvas_drawRenderNode },
 
     { "nCreateDisplayListRenderer", "()J",     (void*) android_view_GLES20Canvas_createDisplayListRenderer },
 
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index 0210bd9..5ebed9c 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -142,16 +142,16 @@
 // Canvas management
 // ----------------------------------------------------------------------------
 
-static inline SkBitmap::Config convertPixelFormat(int32_t format) {
+static inline SkColorType convertPixelFormat(int32_t format) {
     switch (format) {
         case PIXEL_FORMAT_RGBA_8888:
-            return SkBitmap::kARGB_8888_Config;
+            return kN32_SkColorType;
         case PIXEL_FORMAT_RGBX_8888:
-            return SkBitmap::kARGB_8888_Config;
+            return kN32_SkColorType;
         case PIXEL_FORMAT_RGB_565:
-            return SkBitmap::kRGB_565_Config;
+            return kRGB_565_SkColorType;
         default:
-            return SkBitmap::kNo_Config;
+            return kUnknown_SkColorType;
     }
 }
 
@@ -188,8 +188,10 @@
     ssize_t bytesCount = buffer->getStride() * bytesPerPixel(buffer->getPixelFormat());
 
     SkBitmap bitmap;
-    bitmap.setConfig(convertPixelFormat(buffer->getPixelFormat()),
-            buffer->getWidth(), buffer->getHeight(), bytesCount);
+    bitmap.setInfo(SkImageInfo::Make(buffer->getWidth(), buffer->getHeight(),
+                                     convertPixelFormat(buffer->getPixelFormat()),
+                                     kPremul_SkAlphaType),
+                   bytesCount);
 
     if (buffer->getWidth() > 0 && buffer->getHeight() > 0) {
         bitmap.setPixels(bits);
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index 3d14aaf..7018751 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -173,17 +173,17 @@
     return value;
 }
 
-static inline SkBitmap::Config convertPixelFormat(PixelFormat format) {
+static inline SkColorType convertPixelFormat(PixelFormat format) {
     /* note: if PIXEL_FORMAT_RGBX_8888 means that all alpha bytes are 0xFF, then
-        we can map to SkBitmap::kARGB_8888_Config, and optionally call
+        we can map to kN32_SkColorType, and optionally call
         bitmap.setAlphaType(kOpaque_SkAlphaType) on the resulting SkBitmap
         (as an accelerator)
     */
     switch (format) {
-    case PIXEL_FORMAT_RGBX_8888:    return SkBitmap::kARGB_8888_Config;
-    case PIXEL_FORMAT_RGBA_8888:    return SkBitmap::kARGB_8888_Config;
-    case PIXEL_FORMAT_RGB_565:      return SkBitmap::kRGB_565_Config;
-    default:                        return SkBitmap::kNo_Config;
+    case PIXEL_FORMAT_RGBX_8888:    return kN32_SkColorType;
+    case PIXEL_FORMAT_RGBA_8888:    return kN32_SkColorType;
+    case PIXEL_FORMAT_RGB_565:      return kRGB_565_SkColorType;
+    default:                        return kUnknown_SkColorType;
     }
 }
 
@@ -220,12 +220,16 @@
     // Associate a SkCanvas object to this surface
     env->SetIntField(canvasObj, gCanvasClassInfo.mSurfaceFormat, outBuffer.format);
 
+    SkImageInfo info = SkImageInfo::Make(outBuffer.width, outBuffer.height,
+                                         convertPixelFormat(outBuffer.format),
+                                         kPremul_SkAlphaType);
+    if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
+        info.fAlphaType = kOpaque_SkAlphaType;
+    }
+
     SkBitmap bitmap;
     ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
-    bitmap.setConfig(convertPixelFormat(outBuffer.format), outBuffer.width, outBuffer.height, bpr);
-    if (outBuffer.format == PIXEL_FORMAT_RGBX_8888) {
-        bitmap.setAlphaType(kOpaque_SkAlphaType);
-    }
+    bitmap.setInfo(info, bpr);
     if (outBuffer.width > 0 && outBuffer.height > 0) {
         bitmap.setPixels(outBuffer.bits);
     } else {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index c0d5221..9783e91 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -61,6 +61,8 @@
     jfieldID xDpi;
     jfieldID yDpi;
     jfieldID secure;
+    jfieldID appVsyncOffsetNanos;
+    jfieldID presentationDeadlineNanos;
 } gPhysicalDisplayInfoClassInfo;
 
 static struct {
@@ -173,7 +175,7 @@
             screenshot->getStride() * android::bytesPerPixel(screenshot->getFormat());
 
     SkBitmap* bitmap = new SkBitmap();
-    bitmap->setConfig(screenshotInfo, (size_t)rowBytes);
+    bitmap->setInfo(screenshotInfo, (size_t)rowBytes);
     if (screenshotInfo.fWidth > 0 && screenshotInfo.fHeight > 0) {
         // takes ownership of ScreenshotClient
         SkMallocPixelRef* pixels = SkMallocPixelRef::NewWithProc(screenshotInfo,
@@ -392,6 +394,10 @@
         env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.xDpi, info.xdpi);
         env->SetFloatField(infoObj, gPhysicalDisplayInfoClassInfo.yDpi, info.ydpi);
         env->SetBooleanField(infoObj, gPhysicalDisplayInfoClassInfo.secure, info.secure);
+        env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos,
+                info.appVsyncOffset);
+        env->SetLongField(infoObj, gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos,
+                info.presentationDeadline);
         env->SetObjectArrayElement(configArray, static_cast<jsize>(c), infoObj);
         env->DeleteLocalRef(infoObj);
     }
@@ -648,6 +654,10 @@
     gPhysicalDisplayInfoClassInfo.xDpi = env->GetFieldID(clazz, "xDpi", "F");
     gPhysicalDisplayInfoClassInfo.yDpi = env->GetFieldID(clazz, "yDpi", "F");
     gPhysicalDisplayInfoClassInfo.secure = env->GetFieldID(clazz, "secure", "Z");
+    gPhysicalDisplayInfoClassInfo.appVsyncOffsetNanos = env->GetFieldID(clazz,
+            "appVsyncOffsetNanos", "J");
+    gPhysicalDisplayInfoClassInfo.presentationDeadlineNanos = env->GetFieldID(clazz,
+            "presentationDeadlineNanos", "J");
 
     jclass rectClazz = env->FindClass("android/graphics/Rect");
     gRectClassInfo.bottom = env->GetFieldID(rectClazz, "bottom", "I");
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index c1ab515..5c04a78 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -73,17 +73,28 @@
 // Native layer
 // ----------------------------------------------------------------------------
 
-static inline SkBitmap::Config convertPixelFormat(int32_t format) {
-    switch (format) {
+// FIXME: consider exporting this to share (e.g. android_view_Surface.cpp)
+static inline SkImageInfo convertPixelFormat(const ANativeWindow_Buffer& buffer) {
+    SkImageInfo info;
+    info.fWidth = buffer.width;
+    info.fHeight = buffer.height;
+    switch (buffer.format) {
         case WINDOW_FORMAT_RGBA_8888:
-            return SkBitmap::kARGB_8888_Config;
+            info.fColorType = kN32_SkColorType;
+            info.fAlphaType = kPremul_SkAlphaType;
+            break;
         case WINDOW_FORMAT_RGBX_8888:
-            return SkBitmap::kARGB_8888_Config;
+            info.fColorType = kN32_SkColorType;
+            info.fAlphaType = kOpaque_SkAlphaType;
         case WINDOW_FORMAT_RGB_565:
-            return SkBitmap::kRGB_565_Config;
+            info.fColorType = kRGB_565_SkColorType;
+            info.fAlphaType = kOpaque_SkAlphaType;
         default:
-            return SkBitmap::kNo_Config;
+            info.fColorType = kUnknown_SkColorType;
+            info.fAlphaType = kIgnore_SkAlphaType;
+            break;
     }
+    return info;
 }
 
 /**
@@ -148,11 +159,7 @@
     ssize_t bytesCount = buffer.stride * bytesPerPixel(buffer.format);
 
     SkBitmap bitmap;
-    bitmap.setConfig(convertPixelFormat(buffer.format), buffer.width, buffer.height, bytesCount);
-
-    if (buffer.format == WINDOW_FORMAT_RGBX_8888) {
-        bitmap.setAlphaType(kOpaque_SkAlphaType);
-    }
+    bitmap.setInfo(convertPixelFormat(buffer), bytesCount);
 
     if (buffer.width > 0 && buffer.height > 0) {
         bitmap.setPixels(buffer.bits);
diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp
index 815c4a7..3e62d0b 100644
--- a/core/jni/android_view_ThreadedRenderer.cpp
+++ b/core/jni/android_view_ThreadedRenderer.cpp
@@ -49,6 +49,14 @@
 
 static jmethodID gRunnableMethod;
 
+static JNIEnv* getenv(JavaVM* vm) {
+    JNIEnv* env;
+    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+        LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm);
+    }
+    return env;
+}
+
 class JavaTask : public RenderTask {
 public:
     JavaTask(JNIEnv* env, jobject jrunnable) {
@@ -57,44 +65,17 @@
     }
 
     virtual void run() {
-        env()->CallVoidMethod(mRunnable, gRunnableMethod);
-        env()->DeleteGlobalRef(mRunnable);
+        JNIEnv* env = getenv(mVm);
+        env->CallVoidMethod(mRunnable, gRunnableMethod);
+        env->DeleteGlobalRef(mRunnable);
         delete this;
     };
 
 private:
-    JNIEnv* env() {
-        JNIEnv* env;
-        if (mVm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
-            return 0;
-        }
-        return env;
-    }
-
     JavaVM* mVm;
     jobject mRunnable;
 };
 
-class SetAtlasTask : public RenderTask {
-public:
-    SetAtlasTask(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size)
-            : mBuffer(buffer)
-            , mMap(map)
-            , mMapSize(size) {
-    }
-
-    virtual void run() {
-        CanvasContext::setTextureAtlas(mBuffer, mMap, mMapSize);
-        mMap = 0;
-        delete this;
-    }
-
-private:
-    sp<GraphicBuffer> mBuffer;
-    int64_t* mMap;
-    size_t mMapSize;
-};
-
 class OnFinishedEvent {
 public:
     OnFinishedEvent(BaseRenderNodeAnimator* animator, AnimationListener* listener)
@@ -122,12 +103,34 @@
     std::vector<OnFinishedEvent> mOnFinishedEvents;
 };
 
-class RootRenderNode : public RenderNode, public AnimationHook {
+class RenderingException : public MessageHandler {
 public:
-    RootRenderNode() : RenderNode() {
+    RenderingException(JavaVM* vm, const std::string& message)
+            : mVm(vm)
+            , mMessage(message) {
+    }
+
+    virtual void handleMessage(const Message&) {
+        throwException(mVm, mMessage);
+    }
+
+    static void throwException(JavaVM* vm, const std::string& message) {
+        JNIEnv* env = getenv(vm);
+        jniThrowException(env, "java/lang/IllegalStateException", message.c_str());
+    }
+
+private:
+    JavaVM* mVm;
+    std::string mMessage;
+};
+
+class RootRenderNode : public RenderNode, AnimationHook, ErrorHandler {
+public:
+    RootRenderNode(JNIEnv* env) : RenderNode() {
         mLooper = Looper::getForThread();
         LOG_ALWAYS_FATAL_IF(!mLooper.get(),
                 "Must create RootRenderNode on a thread with a looper!");
+        env->GetJavaVM(&mVm);
     }
 
     virtual ~RootRenderNode() {}
@@ -137,10 +140,16 @@
         mOnFinishedEvents.push_back(event);
     }
 
+    virtual void onError(const std::string& message) {
+        mLooper->sendMessage(new RenderingException(mVm, message), 0);
+    }
+
     virtual void prepareTree(TreeInfo& info) {
         info.animationHook = this;
+        info.errorHandler = this;
         RenderNode::prepareTree(info);
         info.animationHook = NULL;
+        info.errorHandler = NULL;
 
         // post all the finished stuff
         if (mOnFinishedEvents.size()) {
@@ -160,10 +169,11 @@
 private:
     sp<Looper> mLooper;
     std::vector<OnFinishedEvent> mOnFinishedEvents;
+    JavaVM* mVm;
 };
 
 static void android_view_ThreadedRenderer_setAtlas(JNIEnv* env, jobject clazz,
-        jobject graphicBuffer, jlongArray atlasMapArray) {
+        jlong proxyPtr, jobject graphicBuffer, jlongArray atlasMapArray) {
     sp<GraphicBuffer> buffer = graphicBufferForJavaObject(env, graphicBuffer);
     jsize len = env->GetArrayLength(atlasMapArray);
     if (len <= 0) {
@@ -173,12 +183,12 @@
     int64_t* map = new int64_t[len];
     env->GetLongArrayRegion(atlasMapArray, 0, len, map);
 
-    SetAtlasTask* task = new SetAtlasTask(buffer, map, len);
-    RenderThread::getInstance().queue(task);
+    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+    proxy->setTextureAtlas(buffer, map, len);
 }
 
 static jlong android_view_ThreadedRenderer_createRootRenderNode(JNIEnv* env, jobject clazz) {
-    RootRenderNode* node = new RootRenderNode();
+    RootRenderNode* node = new RootRenderNode(env);
     node->incStrong(0);
     node->setName("RootRenderNode");
     return reinterpret_cast<jlong>(node);
@@ -261,10 +271,9 @@
 }
 
 static void android_view_ThreadedRenderer_invokeFunctor(JNIEnv* env, jobject clazz,
-        jlong proxyPtr, jlong functorPtr, jboolean waitForCompletion) {
-    RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr);
+        jlong functorPtr, jboolean waitForCompletion) {
     Functor* functor = reinterpret_cast<Functor*>(functorPtr);
-    proxy->invokeFunctor(functor, waitForCompletion);
+    RenderProxy::invokeFunctor(functor, waitForCompletion);
 }
 
 static void android_view_ThreadedRenderer_runWithGlContext(JNIEnv* env, jobject clazz,
@@ -357,7 +366,7 @@
 
 static JNINativeMethod gMethods[] = {
 #ifdef USE_OPENGL_RENDERER
-    { "nSetAtlas", "(Landroid/view/GraphicBuffer;[J)V",   (void*) android_view_ThreadedRenderer_setAtlas },
+    { "nSetAtlas", "(JLandroid/view/GraphicBuffer;[J)V",   (void*) android_view_ThreadedRenderer_setAtlas },
     { "nCreateRootRenderNode", "()J", (void*) android_view_ThreadedRenderer_createRootRenderNode },
     { "nCreateProxy", "(ZJ)J", (void*) android_view_ThreadedRenderer_createProxy },
     { "nDeleteProxy", "(J)V", (void*) android_view_ThreadedRenderer_deleteProxy },
@@ -370,7 +379,7 @@
     { "nSetOpaque", "(JZ)V", (void*) android_view_ThreadedRenderer_setOpaque },
     { "nSyncAndDrawFrame", "(JJJF)I", (void*) android_view_ThreadedRenderer_syncAndDrawFrame },
     { "nDestroyCanvasAndSurface", "(J)V", (void*) android_view_ThreadedRenderer_destroyCanvasAndSurface },
-    { "nInvokeFunctor", "(JJZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
+    { "nInvokeFunctor", "(JZ)V", (void*) android_view_ThreadedRenderer_invokeFunctor },
     { "nRunWithGlContext", "(JLjava/lang/Runnable;)V", (void*) android_view_ThreadedRenderer_runWithGlContext },
     { "nCreateDisplayListLayer", "(JII)J", (void*) android_view_ThreadedRenderer_createDisplayListLayer },
     { "nCreateTextureLayer", "(J)J", (void*) android_view_ThreadedRenderer_createTextureLayer },
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index edca101..fa1a563 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -657,6 +657,13 @@
         android:label="@string/permlab_addVoicemail"
         android:description="@string/permdesc_addVoicemail" />
 
+    <!-- Allows an application to read all the voicemails in the system. -->
+    <permission android:name="com.android.voicemail.permission.READ_ALL_VOICEMAIL"
+        android:permissionGroup="android.permission-group.VOICEMAIL"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_readAllVoicemail"
+        android:description="@string/permdesc_readAllVoicemail" />
+
     <!-- =============================================== -->
     <!-- Permissions for enabling accessibility features -->
     <!-- =============================================== -->
@@ -2735,7 +2742,8 @@
                 android:finishOnCloseSystemDialogs="true"
                 android:excludeFromRecents="true"
                 android:multiprocess="true"
-                android:documentLaunchMode="never">
+                android:documentLaunchMode="never"
+                android:relinquishTaskIdentity="true">
             <intent-filter>
                 <action android:name="android.intent.action.CHOOSER" />
                 <category android:name="android.intent.category.DEFAULT" />
@@ -2928,6 +2936,12 @@
                  android:permission="android.permission.BIND_JOB_SERVICE" >
         </service>
 
+        <service
+            android:name="com.android.server.pm.BackgroundDexOptService"
+            android:exported="true"
+            android:permission="android.permission.BIND_JOB_SERVICE">
+        </service>
+
     </application>
 
 </manifest>
diff --git a/core/res/res/anim/lock_screen_behind_enter.xml b/core/res/res/anim/lock_screen_behind_enter.xml
index 7e212be..ace17ae 100644
--- a/core/res/res/anim/lock_screen_behind_enter.xml
+++ b/core/res/res/anim/lock_screen_behind_enter.xml
@@ -18,7 +18,7 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android"
-    android:detachWallpaper="true" android:shareInterpolator="false" android:startOffset="60">
+    android:detachWallpaper="true" android:shareInterpolator="false" android:startOffset="100">
     <alpha
         android:fromAlpha="0.0" android:toAlpha="1.0"
         android:fillEnabled="true" android:fillBefore="true"
diff --git a/core/res/res/drawable-hdpi/ic_lock_open_wht_24dp.png b/core/res/res/drawable-hdpi/ic_lock_open_wht_24dp.png
new file mode 100644
index 0000000..4d97045
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_open_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lock_outline_wht_24dp.png b/core/res/res/drawable-hdpi/ic_lock_outline_wht_24dp.png
new file mode 100644
index 0000000..46fb463
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lock_outline_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_recent.png b/core/res/res/drawable-hdpi/ic_recent.png
new file mode 100644
index 0000000..8866539
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_recent.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_open_wht_24dp.png b/core/res/res/drawable-mdpi/ic_lock_open_wht_24dp.png
new file mode 100644
index 0000000..163f4a0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_lock_open_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lock_outline_wht_24dp.png b/core/res/res/drawable-mdpi/ic_lock_outline_wht_24dp.png
new file mode 100644
index 0000000..bbfb83c7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_lock_outline_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_recent.png b/core/res/res/drawable-mdpi/ic_recent.png
new file mode 100644
index 0000000..2b607df
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_recent.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lock_open_wht_24dp.png b/core/res/res/drawable-xhdpi/ic_lock_open_wht_24dp.png
new file mode 100644
index 0000000..21d4d53
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lock_open_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lock_outline_wht_24dp.png b/core/res/res/drawable-xhdpi/ic_lock_outline_wht_24dp.png
new file mode 100644
index 0000000..2aeb9a2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lock_outline_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_recent.png b/core/res/res/drawable-xhdpi/ic_recent.png
new file mode 100644
index 0000000..86316db
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_recent.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_lock_open_wht_24dp.png b/core/res/res/drawable-xxhdpi/ic_lock_open_wht_24dp.png
new file mode 100644
index 0000000..1b11b59
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_lock_open_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_lock_outline_wht_24dp.png b/core/res/res/drawable-xxhdpi/ic_lock_outline_wht_24dp.png
new file mode 100644
index 0000000..ae0d655
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_lock_outline_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/ic_recent.png b/core/res/res/drawable-xxhdpi/ic_recent.png
new file mode 100644
index 0000000..e6bd125
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/ic_recent.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_lock_open_wht_24dp.png b/core/res/res/drawable-xxxhdpi/ic_lock_open_wht_24dp.png
new file mode 100644
index 0000000..8774412
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_lock_open_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable-xxxhdpi/ic_lock_outline_wht_24dp.png b/core/res/res/drawable-xxxhdpi/ic_lock_outline_wht_24dp.png
new file mode 100644
index 0000000..1375acc
--- /dev/null
+++ b/core/res/res/drawable-xxxhdpi/ic_lock_outline_wht_24dp.png
Binary files differ
diff --git a/core/res/res/drawable/ic_corp_badge.xml b/core/res/res/drawable/ic_corp_badge.xml
index 5325712..16c101b 100644
--- a/core/res/res/drawable/ic_corp_badge.xml
+++ b/core/res/res/drawable/ic_corp_badge.xml
@@ -15,20 +15,29 @@
 -->
 <vector xmlns:android="http://schemas.android.com/apk/res/android" >
     <size
-        android:width="19.0dp"
-        android:height="19.0dp"/>
+        android:width="20.0dp"
+        android:height="20.0dp"/>
 
     <viewport
-        android:viewportWidth="19.0"
-        android:viewportHeight="19.0"/>
+        android:viewportWidth="20.0"
+        android:viewportHeight="20.0"/>
 
     <path
-        android:pathData="M9.5,9.5m-9.5,0.0a9.5,9.5 0.0,1.0 1.0,19.0 0.0a9.5,9.5 0.0,1.0 1.0,-19.0 0.0"
+        android:pathData="M10.0,10.0m-10.0,0.0a10.0,10.0 0.0,1.0 1.0,20.0 0.0a10.0,10.0 0.0,1.0 1.0,-20.0 0.0"
         android:fill="#FF5722"/>
     <path
-        android:pathData="M12.667,7.125l-1.583,0.0L11.084,6.333l-0.792,-0.792L8.708,5.5410004L7.917,6.333l0.0,0.792L6.333,7.125c-0.438,0.0 -0.788,0.354 -0.788,0.792l-0.004,4.354c0.0,0.438 0.354,0.792 0.792,0.792l6.333,0.0c0.438,0.0 0.792,-0.354 0.792,-0.792L13.458,7.917C13.458,7.479 13.104,7.125 12.667,7.125zM10.094,10.687L8.906,10.687L8.906,9.5l1.188,0.0L10.094,10.687zM10.292,7.125L8.708,7.125L8.708,6.333l1.583,0.0L10.291,7.125z"
+        android:pathData="M11.139,12.149l-0.001,0.0L8.996,12.149l0.0,-0.571L4.738,11.578l-0.002,2.198c0.0,0.589 0.477,1.066 1.066,1.066l8.535,0.0c0.589,0.0 1.066,-0.477 1.066,-1.066l0.0,-2.198l-4.264,0.0L11.139,12.149z"
         android:fill="#FFFFFF"/>
     <path
-        android:pathData="M4.75,4.75 h9.5 v9.5 h-9.5z"
+        android:pathData="M8.996,10.006l2.143,0.0l0.0,0.52l4.442,0.0L15.580999,7.909c0.0,-0.589 -0.477,-1.066 -1.066,-1.066l-1.877,0.0L7.544,6.843L5.606,6.843c-0.589,0.0 -1.061,0.477 -1.061,1.066l-0.003,2.617l4.453,0.0L8.996,10.006L8.996,10.006z"
+        android:fill="#FFFFFF"/>
+    <path
+        android:pathData="M3.367,3.456 h13.016 v13.016 h-13.016z"
+        android:fill="#00000000"/>
+    <path
+        android:pathData="M7.368,5.263l5.263,0.0l0.0,1.053l-5.263,0.0z"
+        android:fill="#FFFFFF"/>
+    <path
+        android:pathData="M8.996,12.149l2.1419992,0.0 0.0010004044,0.0 0.0,-0.5699997 -2.1429996,0.0z"
         android:fill="#00000000"/>
 </vector>
diff --git a/core/res/res/drawable/ic_corp_icon_badge.xml b/core/res/res/drawable/ic_corp_icon_badge.xml
index 7bfab4c..c8e49e1 100644
--- a/core/res/res/drawable/ic_corp_icon_badge.xml
+++ b/core/res/res/drawable/ic_corp_icon_badge.xml
@@ -24,17 +24,31 @@
 
     <path
         android:fill="#FF000000"
-        android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"/>
+        android:pathData="M49.062,50.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"
+        android:fillOpacity="0.2"/>
     <path
         android:fill="#FF000000"
-        android:pathData="M49.0,49.5m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"/>
+        android:pathData="M49.0,49.5m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"
+        android:fillOpacity="0.2"/>
     <path
         android:pathData="M49.0,49.0m-14.0,0.0a14.0,14.0 0.0,1.0 1.0,28.0 0.0a14.0,14.0 0.0,1.0 1.0,-28.0 0.0"
         android:fill="#FF5722"/>
     <path
-        android:pathData="M53.667,45.5l-2.333,0.0l0.0,-1.167l-1.167,-1.167l-2.333,0.0l-1.167,1.167L46.667,45.5l-2.333,0.0c-0.645,0.0 -1.161,0.522 -1.161,1.167l-0.006,6.417c0.0,0.645 0.522,1.167 1.167,1.167l9.333,0.0c0.645,0.0 1.167,-0.522 1.167,-1.167l0.0,-6.417C54.833,46.022 54.311,45.5 53.667,45.5zM49.875,50.75l-1.75,0.0L48.125,49.0l1.75,0.0L49.875,50.75zM50.167,45.5l-2.333,0.0l0.0,-1.167l2.333,0.0L50.167,45.5z"
+        android:pathData="M50.594,52.009l-3.0,0.0L47.594,51.0l-5.961,0.0l-0.003,3.289c0.0,0.826 0.668,1.494 1.494,1.494l11.948,0.0c0.826,0.0 1.494,-0.668 1.494,-1.494L56.566006,51.0l-5.972,0.0C50.594,51.0 50.594,52.009 50.594,52.009z"
         android:fill="#FFFFFF"/>
     <path
-        android:pathData="M42.0,42.0 h14.0 v14.0 h-14.0z"
+        android:pathData="M47.594,49.009l3.0,0.0L50.594,50.0l6.22,0.0l0.0,-3.925c0.0,-0.826 -0.668,-1.494 -1.494,-1.494l-2.627,0.0l-7.131,-0.001l-2.713,0.0c-0.826,0.0 -1.486,0.668 -1.486,1.494L41.359,50.0l6.235,0.0L47.594,49.009z"
+        android:fill="#FFFFFF"/>
+    <path
+        android:pathData="M39.714,39.838 h18.221 v18.221 h-18.221z"
         android:fill="#00000000"/>
+    <path
+        android:pathData="M47.594,49.009 h3.0 v0.991 h-3.0z"
+        android:fill="#00000000"/>
+    <path
+        android:pathData="M47.594,51.0 h3.0 v1.009 h-3.0z"
+        android:fill="#00000000"/>
+    <path
+        android:pathData="M46.0,43.0l6.0,0.0l0.0,1.0l-6.0,0.0z"
+        android:fill="#FFFFFF"/>
 </vector>
diff --git a/core/res/res/drawable/lock_task_notify_bg.xml b/core/res/res/drawable/lock_task_notify_bg.xml
new file mode 100644
index 0000000..3a8fab5
--- /dev/null
+++ b/core/res/res/drawable/lock_task_notify_bg.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle" >
+
+    <solid android:color="@android:color/black" />
+    <corners
+        android:radius="8dip" />
+</shape>
diff --git a/core/res/res/drawable/view_accessibility_focused.xml b/core/res/res/drawable/view_accessibility_focused.xml
index 0da9a81..68e3f1e 100644
--- a/core/res/res/drawable/view_accessibility_focused.xml
+++ b/core/res/res/drawable/view_accessibility_focused.xml
@@ -17,9 +17,11 @@
 <shape xmlns:android="http://schemas.android.com/apk/res/android" >
 
     <stroke
-        android:width="2dp"
-        android:color="@color/accessibility_focus_highlight" />
+        android:width="4dp"
+        android:color="@color/accessibility_focus_highlight"
+        android:dashWidth="4dp"
+        android:dashGap="2dp" />
 
-    <corners android:radius="2dp"/>
+    <corners android:radius="2dp" />
 
 </shape>
diff --git a/core/res/res/layout/alert_dialog_leanback.xml b/core/res/res/layout/alert_dialog_leanback.xml
index 8655aea..848015c 100644
--- a/core/res/res/layout/alert_dialog_leanback.xml
+++ b/core/res/res/layout/alert_dialog_leanback.xml
@@ -1,20 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
-** Copyright 2014, 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.
-*/
+     Copyright (C) 2014 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
@@ -22,11 +20,13 @@
     android:id="@+id/parentPanel"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:background="@drawable/dialog_background_material"
+    android:translationZ="@dimen/floating_window_z"
     android:layout_marginLeft="@dimen/leanback_alert_dialog_horizontal_margin"
-    android:layout_marginRight="@dimen/leanback_alert_dialog_horizontal_margin"
     android:layout_marginTop="@dimen/leanback_alert_dialog_vertical_margin"
-    android:layout_marginBottom="@dimen/leanback_alert_dialog_vertical_margin"
-    android:orientation="vertical">
+    android:layout_marginRight="@dimen/leanback_alert_dialog_horizontal_margin"
+    android:layout_marginBottom="@dimen/leanback_alert_dialog_vertical_margin">
 
     <LinearLayout android:id="@+id/topPanel"
         android:layout_width="match_parent"
@@ -37,16 +37,17 @@
             android:layout_height="wrap_content"
             android:orientation="horizontal"
             android:gravity="center_vertical|start"
-            android:minHeight="@dimen/alert_dialog_title_height"
-            android:layout_marginStart="16dip"
-            android:layout_marginEnd="16dip">
+            android:paddingStart="16dip"
+            android:paddingEnd="16dip"
+            android:paddingTop="16dip">
             <ImageView android:id="@+id/icon"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:paddingEnd="8dip"
+                android:layout_width="32dip"
+                android:layout_height="32dip"
+                android:layout_marginEnd="8dip"
+                android:scaleType="fitCenter"
                 android:src="@null" />
-            <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
-                style="?android:attr/windowTitleStyle"
+            <TextView android:id="@+id/alertTitle"
+                style="?attr/windowTitleStyle"
                 android:singleLine="true"
                 android:ellipsize="end"
                 android:layout_width="match_parent"
@@ -67,13 +68,12 @@
             android:layout_height="wrap_content"
             android:clipToPadding="false">
             <TextView android:id="@+id/message"
-                style="?android:attr/textAppearanceMedium"
+                style="?attr/textAppearanceMedium"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:paddingStart="16dip"
                 android:paddingEnd="16dip"
-                android:paddingTop="8dip"
-                android:paddingBottom="8dip"/>
+                android:paddingTop="16dip" />
         </ScrollView>
     </LinearLayout>
 
@@ -91,41 +91,32 @@
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:minHeight="@dimen/alert_dialog_button_bar_height"
-        android:orientation="vertical">
+        android:orientation="vertical"
+        android:gravity="end"
+        android:padding="16dip">
         <LinearLayout
-            style="?android:attr/buttonBarStyle"
-            android:layout_width="match_parent"
+            style="?attr/buttonBarStyle"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-            android:orientation="horizontal"
-            android:layoutDirection="locale"
-            android:measureWithLargestChild="true">
-            <Button android:id="@+id/button2"
-                android:layout_width="wrap_content"
-                android:layout_gravity="start"
-                android:layout_weight="1"
-                android:maxLines="2"
-                style="?android:attr/buttonBarButtonStyle"
-                android:textSize="14sp"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                android:layout_height="wrap_content" />
+            android:layoutDirection="locale">
             <Button android:id="@+id/button3"
+                style="?attr/buttonBarButtonStyle"
                 android:layout_width="wrap_content"
-                android:layout_gravity="center_horizontal"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:maxLines="2"
-                style="?android:attr/buttonBarButtonStyle"
-                android:textSize="14sp"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                android:layout_height="wrap_content" />
+                android:minHeight="@dimen/alert_dialog_button_bar_height" />
+            <Button android:id="@+id/button2"
+                style="?attr/buttonBarButtonStyle"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:maxLines="2"
+                android:minHeight="@dimen/alert_dialog_button_bar_height" />
             <Button android:id="@+id/button1"
+                style="?attr/buttonBarButtonStyle"
                 android:layout_width="wrap_content"
-                android:layout_gravity="end"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:maxLines="2"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                style="?android:attr/buttonBarButtonStyle"
-                android:textSize="14sp"
-                android:layout_height="wrap_content" />
+                android:minHeight="@dimen/alert_dialog_button_bar_height" />
         </LinearLayout>
      </LinearLayout>
 </LinearLayout>
diff --git a/core/res/res/layout/alert_dialog_leanback_button_panel_right.xml b/core/res/res/layout/alert_dialog_leanback_button_panel_right.xml
index 096b015..829d5aa 100644
--- a/core/res/res/layout/alert_dialog_leanback_button_panel_right.xml
+++ b/core/res/res/layout/alert_dialog_leanback_button_panel_right.xml
@@ -1,20 +1,18 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!--
-/*
-** Copyright 2014, 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.
-*/
+     Copyright (C) 2014 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
@@ -22,18 +20,20 @@
     android:id="@+id/parentPanel"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
+    android:orientation="horizontal"
+    android:background="@drawable/dialog_background_material"
+    android:translationZ="@dimen/floating_window_z"
     android:layout_marginLeft="@dimen/leanback_alert_dialog_horizontal_margin"
-    android:layout_marginRight="@dimen/leanback_alert_dialog_horizontal_margin"
     android:layout_marginTop="@dimen/leanback_alert_dialog_vertical_margin"
-    android:layout_marginBottom="@dimen/leanback_alert_dialog_vertical_margin"
-    android:orientation="horizontal">
+    android:layout_marginRight="@dimen/leanback_alert_dialog_horizontal_margin"
+    android:layout_marginBottom="@dimen/leanback_alert_dialog_vertical_margin">
 
-  <LinearLayout
-      android:id="@+id/leftPanel"
-      android:layout_width="0dp"
-      android:layout_weight="0.66"
-      android:layout_height="wrap_content"
-      android:orientation="vertical">
+   <LinearLayout
+       android:id="@+id/leftPanel"
+       android:layout_width="0dp"
+       android:layout_weight="1"
+       android:layout_height="wrap_content"
+       android:orientation="vertical">
 
     <LinearLayout android:id="@+id/topPanel"
         android:layout_width="match_parent"
@@ -44,16 +44,17 @@
             android:layout_height="wrap_content"
             android:orientation="horizontal"
             android:gravity="center_vertical|start"
-            android:minHeight="@dimen/alert_dialog_title_height"
-            android:layout_marginStart="16dip"
-            android:layout_marginEnd="16dip">
+            android:paddingStart="16dip"
+            android:paddingEnd="16dip"
+            android:paddingTop="16dip">
             <ImageView android:id="@+id/icon"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:paddingEnd="8dip"
+                android:layout_width="32dip"
+                android:layout_height="32dip"
+                android:layout_marginEnd="8dip"
+                android:scaleType="fitCenter"
                 android:src="@null" />
-            <com.android.internal.widget.DialogTitle android:id="@+id/alertTitle"
-                style="?android:attr/windowTitleStyle"
+            <TextView android:id="@+id/alertTitle"
+                style="?attr/windowTitleStyle"
                 android:singleLine="true"
                 android:ellipsize="end"
                 android:layout_width="match_parent"
@@ -66,6 +67,7 @@
     <LinearLayout android:id="@+id/contentPanel"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
+        android:layout_weight="1"
         android:orientation="vertical"
         android:minHeight="64dp">
         <ScrollView android:id="@+id/scrollView"
@@ -73,13 +75,13 @@
             android:layout_height="wrap_content"
             android:clipToPadding="false">
             <TextView android:id="@+id/message"
-                style="?android:attr/textAppearanceMedium"
+                style="?attr/textAppearanceMedium"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:paddingStart="16dip"
                 android:paddingEnd="16dip"
-                android:paddingTop="8dip"
-                android:paddingBottom="8dip"/>
+                android:paddingTop="16dip"
+		android:paddingBottom="16dip" />
         </ScrollView>
     </LinearLayout>
 
@@ -92,51 +94,41 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content" />
     </FrameLayout>
-  </LinearLayout>
+    </LinearLayout>
 
     <LinearLayout android:id="@+id/buttonPanel"
-        android:layout_width="0dp"
-	android:layout_weight="0.33"
-        android:layout_height="match_parent"
+        android:layout_width="wrap_content"
+android:background="#ffffff"
+        android:layout_height="wrap_content"
+	android:layout_gravity="center_vertical"
         android:minHeight="@dimen/alert_dialog_button_bar_height"
-	android:paddingLeft="32dp"
-	android:paddingRight="32dp"
-        android:orientation="horizontal">
+        android:orientation="vertical"
+        android:gravity="end"
+        android:padding="16dip">
         <LinearLayout
-            style="?android:attr/buttonBarStyle"
-            android:layout_width="match_parent"
+            style="?attr/buttonBarStyle"
+            android:layout_width="wrap_content"
             android:layout_height="wrap_content"
-	    android:layout_gravity="center_vertical"
-            android:orientation="vertical"
             android:layoutDirection="locale"
-            android:measureWithLargestChild="true">
-            <Button android:id="@+id/button1"
-                android:layout_width="match_parent"
-                android:gravity="center_vertical"
-                android:layout_weight="1"
-                android:maxLines="2"
-                style="?android:attr/buttonBarButtonStyle"
-                android:textSize="14sp"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                android:layout_height="wrap_content" />
+	    android:orientation="vertical">
             <Button android:id="@+id/button3"
+                style="?attr/buttonBarButtonStyle"
                 android:layout_width="match_parent"
-                android:gravity="center_vertical"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:maxLines="2"
-                style="?android:attr/buttonBarButtonStyle"
-                android:textSize="14sp"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                android:layout_height="wrap_content" />
+                android:minHeight="@dimen/alert_dialog_button_bar_height" />
             <Button android:id="@+id/button2"
+                style="?attr/buttonBarButtonStyle"
                 android:layout_width="match_parent"
-                android:gravity="center_vertical"
-                android:layout_weight="1"
+                android:layout_height="wrap_content"
                 android:maxLines="2"
-                android:minHeight="@dimen/alert_dialog_button_bar_height"
-                style="?android:attr/buttonBarButtonStyle"
-                android:textSize="14sp"
-                android:layout_height="wrap_content" />
+                android:minHeight="@dimen/alert_dialog_button_bar_height" />
+            <Button android:id="@+id/button1"
+                style="?attr/buttonBarButtonStyle"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:maxLines="2"
+                android:minHeight="@dimen/alert_dialog_button_bar_height" />
         </LinearLayout>
      </LinearLayout>
 </LinearLayout>
diff --git a/core/res/res/layout/lock_to_app_enter.xml b/core/res/res/layout/lock_to_app_enter.xml
new file mode 100644
index 0000000..c034536
--- /dev/null
+++ b/core/res/res/layout/lock_to_app_enter.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:paddingLeft="20dp"
+    android:paddingRight="20dp"
+    android:paddingBottom="12dp"
+    android:alpha=".8"
+    android:background="@drawable/lock_task_notify_bg" >
+
+    <ImageView
+        android:id="@+id/lock_icon"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="20dp"
+        android:alpha=".8"
+        android:src="@drawable/ic_lock_outline_wht_24dp" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/lock_icon"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="2dp"
+        android:textColor="@android:color/white"
+        android:alpha=".8"
+        android:text="@string/lock_to_app_start" />
+</RelativeLayout>
diff --git a/core/res/res/layout/lock_to_app_exit.xml b/core/res/res/layout/lock_to_app_exit.xml
new file mode 100644
index 0000000..4a60c80
--- /dev/null
+++ b/core/res/res/layout/lock_to_app_exit.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<RelativeLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:paddingLeft="20dp"
+    android:paddingRight="20dp"
+    android:paddingBottom="12dp"
+    android:alpha=".8"
+    android:background="@drawable/lock_task_notify_bg" >
+
+    <ImageView
+        android:id="@+id/lock_icon"
+        android:layout_width="48dp"
+        android:layout_height="48dp"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="20dp"
+        android:alpha=".8"
+        android:src="@drawable/ic_lock_open_wht_24dp" />
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@+id/lock_icon"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="2dp"
+        android:textColor="@android:color/white"
+        android:alpha=".8"
+        android:text="@string/lock_to_app_exit" />
+</RelativeLayout>
diff --git a/core/res/res/layout/progress_dialog_leanback.xml b/core/res/res/layout/progress_dialog_leanback.xml
deleted file mode 100644
index 6bcad7a..0000000
--- a/core/res/res/layout/progress_dialog_leanback.xml
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2014, 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:orientation="vertical">
-
-    <View
-        android:layout_width="match_parent"
-        android:layout_height="2dip"
-        android:background="@android:color/leanback_dark_gray" />
-    <LinearLayout android:id="@+id/body"
-        android:orientation="horizontal"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:baselineAligned="false"
-        android:padding="16dip">
-
-        <ProgressBar android:id="@android:id/progress"
-            style="?android:attr/progressBarStyle"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:max="10000"
-            android:layout_marginEnd="16dip" />
-
-        <TextView android:id="@+id/message"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_vertical" />
-    </LinearLayout>
-</LinearLayout>
diff --git a/core/res/res/layout/screen_action_bar.xml b/core/res/res/layout/screen_action_bar.xml
index 8bf8416..5acb588 100644
--- a/core/res/res/layout/screen_action_bar.xml
+++ b/core/res/res/layout/screen_action_bar.xml
@@ -34,7 +34,7 @@
         android:layout_height="wrap_content"
         android:layout_alignParentTop="true"
         style="?attr/actionBarStyle"
-        android:viewName="android:action_bar"
+        android:transitionName="android:action_bar"
         android:gravity="top">
         <com.android.internal.widget.ActionBarView
             android:id="@+id/action_bar"
diff --git a/core/res/res/layout/screen_custom_title.xml b/core/res/res/layout/screen_custom_title.xml
index c8952bf..b385bed 100644
--- a/core/res/res/layout/screen_custom_title.xml
+++ b/core/res/res/layout/screen_custom_title.xml
@@ -31,7 +31,7 @@
     <FrameLayout android:id="@android:id/title_container" 
         android:layout_width="match_parent" 
         android:layout_height="?android:attr/windowTitleSize"
-        android:viewName="android:title"
+        android:transitionName="android:title"
         style="?android:attr/windowTitleBackgroundStyle">
     </FrameLayout>
     <FrameLayout android:id="@android:id/content"
diff --git a/core/res/res/layout/screen_toolbar.xml b/core/res/res/layout/screen_toolbar.xml
index 290c7da..56815f8 100644
--- a/core/res/res/layout/screen_toolbar.xml
+++ b/core/res/res/layout/screen_toolbar.xml
@@ -34,7 +34,7 @@
         android:layout_height="wrap_content"
         android:layout_alignParentTop="true"
         style="?attr/actionBarStyle"
-        android:viewName="android:action_bar"
+        android:transitionName="android:action_bar"
         android:gravity="top">
         <Toolbar
             android:id="@+id/action_bar"
diff --git a/core/res/res/transition/explode.xml b/core/res/res/transition/explode.xml
new file mode 100644
index 0000000..fe22284
--- /dev/null
+++ b/core/res/res/transition/explode.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<explode xmlns:android="http://schemas.android.com/apk/res/android"/>
diff --git a/core/res/res/transition/slide_bottom.xml b/core/res/res/transition/slide_bottom.xml
new file mode 100644
index 0000000..46dc0d6
--- /dev/null
+++ b/core/res/res/transition/slide_bottom.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="bottom"/>
diff --git a/core/res/res/transition/slide_left.xml b/core/res/res/transition/slide_left.xml
new file mode 100644
index 0000000..997bd97
--- /dev/null
+++ b/core/res/res/transition/slide_left.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="left"/>
diff --git a/core/res/res/transition/slide_right.xml b/core/res/res/transition/slide_right.xml
new file mode 100644
index 0000000..98f8f6a
--- /dev/null
+++ b/core/res/res/transition/slide_right.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="right"/>
diff --git a/core/res/res/transition/slide_top.xml b/core/res/res/transition/slide_top.xml
new file mode 100644
index 0000000..07ab945
--- /dev/null
+++ b/core/res/res/transition/slide_top.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+<slide xmlns:android="http://schemas.android.com/apk/res/android" android:slideEdge="top"/>
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 72b0909..cb93530 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Laat die program toe om \'n alarm in \'n geïnstalleerde wekkerprogram te stel. Sommige wekkerprogramme werk dalk nie met hierdie funksie nie."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"voeg stemboodskap by"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Laat die program toe om boodskappe by te voeg by jou stempos-inkassie."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"lees alle stempos"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Laat die program toe om al jou stemposse te lees."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"verander blaaier se geoligging-toestemmings"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Laat die program toe om die blaaier se geoligging-toestemmings te verander. Kwaadwillige programme kan dit gebruik om hulle toe te laat om ligginginligting aan enige webwerf te stuur."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifieer pakkies"</string>
@@ -1362,10 +1364,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Laat \'n program toe om vir veranderinge in vertrouenstaat te luister."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Voorsien \'n vertroude agent."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Laat \'n program toe om \'n vertroude agent te voorsien."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Begin vertrouensagente se instellingskieslys."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Laat \'n program toe om \'n aktiwiteit te begin wat die vertrouensagent se gedrag verander."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Verbind met \'n vertrouensagentdiens"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Laat \'n program toe om met \'n vertrouensagentdiens te verbind."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Tree in wisselwerking met opdatering- en terugstellingstelsel"</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index da89136..66faf61 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"በተጫነው የማንቂያ ሰዓት መተግበሪያ ውስጥ ማንቅያን ለማደራጀት ለመተግበሪያው ይፈቅዳሉ፡፡አንዳንድ የማንቂያ ሰዓት መተግበሪያዎች ይሄንን ባህሪ ላይፈፅሙ ይችላሉ፡፡"</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"የድምፅ መልዕክት አክል"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"ወደ ድምፅ መልዕክት የገቢ መልዕክትህ መልዕክቶች ለማከል ለመተግበሪያው ይፈቅዳሉ።"</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"ሁሉንም የድምጽ መልዕክት ያነብባል"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"መተግበሪያው ሁሉንም የድምጽ መልዕክቶችዎ እንዲያነባቸው ያስችለዋል።"</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"የአሳሽ ገፀ ሥፍራ ፍቃዶችን ቀይር"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"የአሳሹን የጂኦ-አካባቢ ፍቃዶችን እንዲለውጥ ለመተግበሪያው ይፈቅዳል፡፡ተንኮል አዘል መተግበሪያዎች የመላኪያ አከባቢን መረጃ ወደ አጠራጣሪ የድር ጣቢያዎች ለመፍቀድ ይሄንን ሊጠቀሙበት ይችላሉ፡፡"</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ፓኬጆችን አረጋግጥ"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index 626858f..7d1117b 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"للسماح للتطبيق بضبط المنبه في تطبيق المنبه المثبّت. ربما لا تنفذ بعض تطبيقات المنبه هذه الميزة."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"إضافة بريد صوتي"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"للسماح للتطبيق بإضافة رسائل إلى صندوق البريد الصوتي."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"قراءة جميع رسائل البريد الصوتي"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"للسماح للتطبيق بقراءة جميع رسائل البريد الصوتي."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تعديل أذونات الموقع الجغرافي للمتصفح"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"للسماح لأحد التطبيقات بتعديل أذونات الموقع الجغرافي للمتصفح. يمكن أن تستخدم التطبيقات الضارة هذا للسماح بإرسال معلومات الموقع إلى مواقع ويب عشوائية."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"التحقق من الحزم"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index ca71ce3..b9c4f46 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -189,7 +189,7 @@
     <string name="safeMode" msgid="2788228061547930246">"Безопасен режим"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Системно от Android"</string>
     <string name="user_owner_label" msgid="6465364741001216388">"Лични приложения"</string>
-    <string name="managed_profile_label" msgid="4287077106125758391">"Служебен под Android"</string>
+    <string name="managed_profile_label" msgid="4287077106125758391">"Android Work"</string>
     <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Услуги, които ви струват пари"</string>
     <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Извършват неща, които могат да ви струват пари."</string>
     <string name="permgrouplab_messages" msgid="7521249148445456662">"Вашите съобщения"</string>
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Разрешава на приложението да навие инсталирано приложение будилник. Някои будилници може да не изпълнят тази функция."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"добавяне на гласова поща"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Разрешава на приложението да добавя съобщения към входящата ви гласова поща."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"промяна на разрешенията за местоположение в браузъра"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Разрешава на приложението да променя разрешенията на браузъра за местоположение. Злонамерените приложения могат да използват това, за да изпращат информация за местоположението до произволни уебсайтове."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"проверка на пакетите"</string>
@@ -1362,10 +1366,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Разрешава на приложението да следи за промени в състоянието на надеждност."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Предоставяне на trust agent."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Разрешава на приложението да предоставя trust agent."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Стартиране на менюто за настройки за trust agent."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Разрешава на приложението да стартира активност, която променя поведението на trust agent."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Обвързване с услуга за trust agents"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Разрешава на приложението да се обвърже с услуга за trust agents."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Взаимодействие със системата за актуализации и възстановяване"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index e1eed49..18389b6a 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -265,8 +265,8 @@
     <string name="permdesc_statusBar" msgid="8434669549504290975">"Permet que l\'aplicació desactivi la barra d\'estat o afegeixi i elimini icones del sistema."</string>
     <string name="permlab_statusBarService" msgid="7247281911387931485">"barra d\'estat"</string>
     <string name="permdesc_statusBarService" msgid="716113660795976060">"Permet que l\'aplicació sigui la barra d\'estat."</string>
-    <string name="permlab_expandStatusBar" msgid="1148198785937489264">"ampliar/reduir la barra d\'estat"</string>
-    <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permet que l\'aplicació ampliï o redueixi la barra d\'estat."</string>
+    <string name="permlab_expandStatusBar" msgid="1148198785937489264">"desplega/contrau la barra d\'estat"</string>
+    <string name="permdesc_expandStatusBar" msgid="6917549437129401132">"Permet que l\'aplicació desplegui o replegui la barra d\'estat."</string>
     <string name="permlab_install_shortcut" msgid="4279070216371564234">"instal·la dreceres"</string>
     <string name="permdesc_install_shortcut" msgid="8341295916286736996">"Permet que una aplicació afegeixi dreceres a la pantalla d\'inici sense la intervenció de l\'usuari."</string>
     <string name="permlab_uninstall_shortcut" msgid="4729634524044003699">"desinstal·la dreceres"</string>
@@ -945,7 +945,7 @@
     <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"S\'ha iniciat la reorganització del widget."</string>
     <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Ha finalitzat la reorganització del widget."</string>
     <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"S\'ha suprimit el widget de <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
-    <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Amplia l\'àrea de desbloqueig."</string>
+    <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Desplega l\'àrea de desbloqueig."</string>
     <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Desbloqueig lliscant el dit"</string>
     <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueig mitjançant patró"</string>
     <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Desbloqueig facial"</string>
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Permet que l\'aplicació defineixi una alarma en una aplicació de despertador instal·lada. És possible que algunes aplicacions de despertador no incorporin aquesta funció."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"afegeix bústia de veu"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permet que l\'aplicació afegeixi missatges a la safata d\'entrada de la bústia de veu."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"accedir a tots els correus de veu"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Permet que l\'aplicació accedeixi a tots els teus correus de veu."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modifica els permisos d\'ubicació geogràfica del navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permet que l\'aplicació modifiqui els permisos d\'ubicació geogràfica del navegador. Les aplicacions malicioses poden utilitzar-ho per enviar la informació d\'ubicació a llocs web arbitraris."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifica paquets"</string>
@@ -1362,10 +1364,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permet que una aplicació escolti els canvis en l\'estat de confiança."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Proporcionar un agent de confiança"</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permet que una aplicació proporcioni un agent de confiança."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Inicia el menú de configuració de l\'agent de confiança."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permet que una aplicació iniciï una activitat que canviï el comportament de l\'agent de confiança."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Enllaçar amb el servei d\'un agent de confiança"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permet que una aplicació es vinculi amb el servei d\'un agent de confiança."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Interacciona amb el sistema de recuperació i amb les actualitzacions"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index cf06dc1..aeb19d0 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Umožňuje aplikaci nastavit budík v nainstalované aplikaci budík. Některé aplikace budík tuto funkci nemusí obsahovat."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"přidat hlasovou zprávu"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Umožňuje aplikaci přidávat zprávy do hlasové schránky."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"číst všechny hlasové zprávy"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Umožňuje aplikaci číst všechny vaše hlasové zprávy."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"změna oprávnění prohlížeče poskytovat informace o zeměpisné poloze"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Umožňuje aplikaci upravit oprávnění funkce geolokace v prohlížeči. Škodlivé aplikace toho mohou využít k odeslání údajů o poloze na libovolné webové stránky."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ověřit balíčky"</string>
@@ -1362,10 +1364,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Umožňuje aplikaci naslouchat změnám ve stavu důvěryhodnosti."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Poskytování zástupce důvěryhodnosti"</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Umožňuje aplikaci poskytnout zástupce důvěryhodnosti."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Spustit nabídku nastavení agenta důvěryhodnosti"</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Umožňuje aplikaci spustit aktivitu, která změní chování agenta důvěryhodnosti."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Vázat se na službu zástupce důvěryhodnosti"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Umožňuje aplikaci vázat se na službu zástupce důvěryhodnosti."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Interakce se systémem aktualizací a obnovení"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index 0aa2a36..2996f75 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Tillader, at appen kan indstille en alarm i en installeret alarmapp. Nogle alarmapps har muligvis ikke denne funktion."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"tilføje telefonsvarer"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Tillader, at appen kan tilføje beskeder på din telefonsvarer."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"læs alle talebeskeder"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Tillader, at appen kan læse alle dine talebeskeder"</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"skifte tilladelser til geografisk placering i Browser"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillader, at appen kan ændre browserens tilladelser angående geografisk placering. Ondsindede apps kan benytte dette til at sende oplysninger om placering til vilkårlige websites."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"bekræft pakker"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index d557d37..fe5c139 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Ermöglicht der App, einen Alarm in einer installierten Wecker-App einzurichten. Einige Wecker-Apps implementieren diese Funktion möglicherweise nicht."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"Mailbox-Nachrichten hinzufügen"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ermöglicht der App, Nachrichten zu Ihrem Mailbox-Posteingang hinzuzufügen"</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"Alle Mailboxnachrichten abrufen"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Ermöglicht der App das Abrufen aller Ihrer Mailboxnachrichten"</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Geolokalisierungsberechtigungen des Browsers ändern"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ermöglicht der App, die Geolokalisierungsberechtigungen des Browsers zu ändern. Schädliche Apps können so Standortinformationen an beliebige Websites senden."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"Pakete überprüfen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index 8205c60..1a70d19 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Επιτρέπει στην εφαρμογή τη ρύθμιση μιας ειδοποίησης σε μια εγκατεστημένη εφαρμογή ξυπνητηριού. Ορισμένες εφαρμογές ξυπνητηριού ενδέχεται να μην μπορούν να ενσωματώσουν αυτήν τη λειτουργία."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"προσθήκη τηλεφωνητή"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Επιτρέπει στην εφαρμογή να προσθέτει μηνύματα στα εισερχόμενα του αυτόματου τηλεφωνητή σας."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"ανάγνωση όλων των μηνυμάτων του αυτόματου τηλεφωνητή"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Επιτρέπει στην εφαρμογή να διαβάσει όλα τα μηνύματα του αυτόματου τηλεφωνητή σας."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"τροποποίηση δικαιωμάτων γεωγραφικής θέσης του Προγράμματος περιήγησης"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Επιτρέπει στην εφαρμογή την τροποποίηση των αδειών γεωτοποθεσίας του Προγράμματος περιήγησης. Τυχόν κακόβουλες εφαρμογές ενδέχεται να το χρησιμοποιήσουν για να επιτρέψουν την αποστολή πληροφοριών τοποθεσίας σε αυθαίρετους ιστότοπους."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"επαλήθευση πακέτων"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index a0c35c8..c659999 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Allows the app to set an alarm in an installed alarm clock app. Some alarm clock apps may not implement this feature."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"add voicemail"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Allows the app to add messages to your voicemail inbox."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"read all voicemail"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Allows the app to read all your voicemails."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modify Browser geo-location permissions"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Allows the app to modify the Browser\'s geo-location permissions. Malicious apps may use this to allow sending location information to arbitrary websites."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verify packages"</string>
diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml
index a0c35c8..c659999 100644
--- a/core/res/res/values-en-rIN/strings.xml
+++ b/core/res/res/values-en-rIN/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Allows the app to set an alarm in an installed alarm clock app. Some alarm clock apps may not implement this feature."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"add voicemail"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Allows the app to add messages to your voicemail inbox."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"read all voicemail"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Allows the app to read all your voicemails."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modify Browser geo-location permissions"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Allows the app to modify the Browser\'s geo-location permissions. Malicious apps may use this to allow sending location information to arbitrary websites."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verify packages"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index b6a7667..b1108bf2 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que la aplicación establezca una alarma en una aplicación de alarma instalada. Es posible que algunas aplicaciones de alarma no incluyan esta función."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"agregar correo de voz"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que la aplicación agregue mensajes a la bandeja de entrada de tu buzón de voz."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modificar los permisos de ubicación geográfica del navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que la aplicación modifique los permisos de ubicación geográfica del navegador. Las aplicaciones maliciosas pueden utilizar esto para permitir el envío de información de ubicación a sitios web arbitrarios."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"Verificar paquetes"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index 1dcbf62..f47f5c0 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que la aplicación establezca una alarma en una aplicación de reloj instalada. Es posible que algunas aplicaciones de reloj no incluyan esta función."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"añadir buzón de voz"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que la aplicación añada mensajes a la bandeja de entrada del buzón de voz."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"consultar todos los mensajes de voz"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Permite que la aplicación consulte todos tus mensajes de voz."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificar los permisos de ubicación geográfica del navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que la aplicación modifique los permisos de ubicación geográfica del navegador. Las aplicaciones malintencionadas pueden usar este permiso para autorizar el envío de información sobre la ubicación a sitios web arbitrarios."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificar paquetes"</string>
diff --git a/core/res/res/values-et-rEE/strings.xml b/core/res/res/values-et-rEE/strings.xml
index 60d036e..0df3aca 100644
--- a/core/res/res/values-et-rEE/strings.xml
+++ b/core/res/res/values-et-rEE/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Võimaldab rakendusel seada installitud äratuskellarakenduses äratuse. Mõned äratuskellarakendused ei pruugi seda funktsiooni juurutada."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"lisa kõneposti"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Võimaldab rakendusel lisada sõnumeid teie kõneposti postkasti."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"kogu kõneposti lugemine"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Võimaldab rakendusel kogu kõneposti lugeda."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Brauseri geolokatsiooniõiguste muutmine"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Võimaldab rakendusel muuta brauseri geolokatsiooniõigusi. Pahatahtlikud rakendused võivad seda kasutada asukohateabe saatmise lubamiseks suvalistele veebisaitidele."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"pakettide kinnitamine"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index b4727bb..d652544 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"‏به برنامه اجازه می‎دهد تا هشداری را در برنامه ساعت زنگدار نصب شده تنظیم کند. برخی از برنامه‎های ساعت زنگدار نمی‎توانند این ویژگی را اعمال کنند."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"افزودن پست صوتی"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"به برنامه اجازه می‌دهد تا پیام‌ها را به صندوق دریافت پست صوتی شما اضافه کند."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"خواندن کل پست صوتی"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"به برنامه اجازه می‌دهد همه پست‌های صوتی‌تان را بخواند."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"تغییر مجوزهای مکان جغرافیایی مرورگر"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"‏به برنامه اجازه می‎دهد تا مجوزهای جغرافیایی مرورگر را تغییر دهد. برنامه‎های مخرب می‎توانند از آن استفاده کنند تا اطلاعات موقعیت مکانی را به سایت‌های وب کتابخانه بفرستند."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"تأیید بسته‌ها"</string>
@@ -1362,10 +1364,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"‏به یک برنامه کاربردی برای گوش دادن به تغییرات در trust اجازه می‌دهد."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"یک عامل مورد اعتماد فراهم می‌آورد."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"به برنامه امکان می‌دهد یک عامل مورد اعتماد فراهم آورد."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"منوی تنظیمات نماینده امانی را راه‌اندازی کنید."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"به برنامه اجازه می‌دهد تا فعالیتی را راه‌اندازی کند که رفتار نماینده امانی را تغییر می‌دهد."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"‏اتصال به یک سرویس trust agent"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"‏به یک برنامه کاربردی برای اتصال به یک سرویس trust agent اجازه می‌دهد."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"تعامل با سیستم به‌روزرسانی و بازیابی"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 02c6da4..802fb24 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -189,8 +189,7 @@
     <string name="safeMode" msgid="2788228061547930246">"Suojattu tila"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android-järjestelmä"</string>
     <string name="user_owner_label" msgid="6465364741001216388">"Omat sovellukset"</string>
-    <!-- no translation found for managed_profile_label (4287077106125758391) -->
-    <skip />
+    <string name="managed_profile_label" msgid="4287077106125758391">"Android Work"</string>
     <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Maksulliset palvelut"</string>
     <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Suorita mahdollisesti maksullisia toimintoja."</string>
     <string name="permgrouplab_messages" msgid="7521249148445456662">"Omat viestit"</string>
@@ -1002,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Antaa sovelluksen asettaa hälytyksen sisäiseen herätyskellosovellukseen. Jotkin herätyskellosovellukset eivät välttämättä käytä tätä ominaisuutta."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"lisää vastaajaviesti"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Antaa sovelluksen lisätä viestejä saapuneisiin vastaajaviesteihin."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"selaimen maantieteellisen sijainnin lupien muokkaaminen"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Antaa sovelluksen muokata Selaimen maantieteellisen sijainnin lupia. Haitalliset sovellukset voivat sallia tällä sijaintitietojen lähettämisen mielivaltaisiin sivustoihin."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"vahvista paketteja"</string>
@@ -1363,10 +1366,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Antaa sovelluksen seurata luottamuksen tilamuutoksia."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Luotettavan tahon tarjoaminen"</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Antaa sovelluksen tarjota luotettavan tahon."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Käynnistä luotettavan tahon asetusvalikko."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Sallii sovelluksen käynnistää toiminnon, joka muuttaa luotettavan tahon toimintaa."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Luotettavaan tahoon sitoutuminen"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Antaa sovelluksen sitoutua luotettavaan tahoon."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Vuorovaikutus päivitys- ja palautusjärjestelmän kanssa"</string>
diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml
index b1292b3..ec4c2d6 100644
--- a/core/res/res/values-fr-rCA/strings.xml
+++ b/core/res/res/values-fr-rCA/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Permet à l\'application de régler la sonnerie d\'une fonction de réveil installée sur votre appareil. Cette fonctionnalité n\'est pas compatible avec toutes les applications de réveils."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"ajouter des messages vocaux"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permet à l\'application d\'ajouter des messages à votre messagerie vocale."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modifier les autorisations de géolocalisation du navigateur"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permet à l\'application de modifier les autorisations de géolocalisation du navigateur. Des applications malveillantes peuvent exploiter cette fonctionnalité pour permettre l\'envoi de données de localisation à des sites Web arbitraires."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"vérifier les paquets"</string>
@@ -1362,10 +1366,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permet à une application de détecter les modifications de l\'état de confiance."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fournir un agent de confiance."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permet à une application de fournir un agent de confiance."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Lancer le menu des paramètres de l\'agent de confiance."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permet à une application de lancer une activité qui modifie le comportement de l\'agent de confiance."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Lier à un service d\'agent de confiance"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permet à une application de se lier à un service d\'agent de confiance."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Interagir avec le système de récupération et de mise à jour"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index f511df0..57ee762 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Permet à l\'application de régler la sonnerie d\'un réveil installé. Cette fonctionnalité n\'est pas disponible sur tous les réveils."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"ajouter un message vocal"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permet à l\'application d\'ajouter des messages à votre messagerie vocale."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"accéder à tous les messages vocaux"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Permet à l\'application d\'accéder à tous vos messages vocaux."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modifier les autorisations de géolocalisation du navigateur"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permet à l\'application de modifier les autorisations de géolocalisation du navigateur. Des applications malveillantes peuvent exploiter cette fonctionnalité pour permettre l\'envoi de données de localisation à des sites Web arbitraires."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"vérifier les packages"</string>
@@ -1362,10 +1364,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permettre à une application de détecter les modifications de l\'état de confiance."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fournir un agent de confiance"</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permettre à une application de fournir un agent de confiance"</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Lancer le menu des paramètres de l\'agent de confiance"</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permet à une application de lancer une activité qui modifie le comportement de l\'agent de confiance."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"S\'associer à un service d\'agent de confiance"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permettre à une application de s\'associer à un service d\'agent de confiance."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Interagir avec le système de récupération et de mise à jour"</string>
diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml
index 470ec41..8c2c025 100644
--- a/core/res/res/values-hi/strings.xml
+++ b/core/res/res/values-hi/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"ऐप्स को इंस्‍टॉल किए गए अलार्म घड़ी ऐप्स में अलार्म सेट करने देता है. हो सकता है कुछ अलार्म घड़ी ऐप्स में यह सुविधा न हो."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"ध्‍वनिमेल जोड़ें"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"ऐप्स  को आपके ध्‍वनिमेल इनबॉक्‍स में संदेश जोड़ने देता है."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ब्राउज़र भौगोलिक-स्थान अनुमतियों को बदलें"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ऐप्स को ब्राउज़र के भौगोलिक-स्‍थान की अनुमतियां संशोधित करने देता है. दुर्भावनापूर्ण ऐप्स इसका उपयोग एकपक्षीय वेबसाइट को स्‍थान जानकारी भेजने में कर सकते हैं."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"पैकेज सत्‍यापि‍त करें"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 7dc8e19..65e64ec 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Omogućuje aplikaciji postavljanje alarma na instaliranoj aplikaciji budilici. Neke aplikacije budilice možda neće primijeniti tu značajku."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"dodaj govornu poštu"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Omogućuje aplikaciji da doda poruke u vašu govornu poštu."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"čitanje svih poruka u govornoj pošti"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Aplikaciji omogućuje čitanje svih vaših poruka u govornoj pošti."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"izmjena dozvola za geolociranje u pregledniku"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Omogućuje aplikaciji promjenu geolokacijskih dozvola preglednika. Zlonamjerne aplikacije mogu to upotrijebiti da bi dopustile slanje podataka o lokaciji nasumičnim web-lokacijama."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"provjeri pakete"</string>
@@ -1362,10 +1364,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Omogućuje aplikaciji praćenje promjena pouzdanog stanja."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Pružanje agenta za pouzdanost."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Omogućuje aplikaciji pružanje agenta za pouzdanost."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Pokretanje izbornika postavki pouzdanog agenta."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Aplikaciji omogućuje pokretanje aktivnosti koja mijenja ponašanje pouzdanog agenta."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Povezivanje s uslugom pouzdanog predstavnika"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Omogućuje aplikaciji povezivanje s uslugom pouzdanog predstavnika."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Interakcija s ažuriranjem i sustavom za oporavak"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 2582737..498e28a 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -401,7 +401,7 @@
     <string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"csatlakozás egy hangvezérlőhöz"</string>
     <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"Lehetővé teszi a használó számára, hogy csatlakozzon egy hangvezérlő szolgáltatás legfelső szintű kezelőfelületéhez. A normál alkalmazásoknak erre soha nincs szükségük."</string>
     <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"hangalapú kulcskifejezések kezelése"</string>
-    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"A használó kezelheti a hangalapú hotwordök felismerésére szolgáló kulcskifejezéseket. Az átlagos alkalmazásoknak nem lehet rá szükségük."</string>
+    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Az engedély birtokosa kezelheti a hangalapú hotwordök felismerésére szolgáló kulcskifejezéseket. Az átlagos alkalmazásoknak nem lehet rá szükségük."</string>
     <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"csatlakozás egy távoli kijelzőhöz"</string>
     <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Lehetővé teszi a használó számára, hogy csatlakozzon egy távoli kijelző legfelső szintű kezelőfelületéhez. A normál alkalmazásoknak erre soha nincs szükségük."</string>
     <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"csatlakozás modulszolgáltatáshoz"</string>
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Lehetővé teszi az alkalmazás számára, hogy ébresztőt állítson be egy telepített ébresztőóra alkalmazásban. Egyes ilyen alkalmazásokban lehet, hogy nem működik ez a funkció."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"hangposta hozzáadása"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Lehetővé teszi az alkalmazás számára, hogy üzeneteket adjon hozzá bejövő hangpostájához."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"az összes hangüzenet olvasása"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Engedélyezi az alkalmazásnak az összes hangüzenet olvasását."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"a böngésző helymeghatározási engedélyeinek módosítása"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Lehetővé teszi az alkalmazás számára, hogy módosítsa a böngésző helymeghatározási engedélyeit. Rosszindulatú alkalmazások ezt arra használhatják, hogy a helyére vonatkozó információkat küldjenek tetszőleges webhelyeknek."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"csomagok ellenőrzése"</string>
diff --git a/core/res/res/values-hy-rAM/strings.xml b/core/res/res/values-hy-rAM/strings.xml
index f0bee2b..97e9ffc1 100644
--- a/core/res/res/values-hy-rAM/strings.xml
+++ b/core/res/res/values-hy-rAM/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Թույլ է տալիս հավելվածին սահմանել զարթուցիչի ծրագրում տեղադրված ազդանշանը: Զարթուցիչի որոշ հավելվածներ չեն կարող կիրառել այս հատկությունը:"</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"ավելացնել ձայնային փոստ"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Թույլ է տալիս հավելվածին ավելացնել հաղորդագրություններ ձեր ձայնային փոստի արկղում:"</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"փոփոխել դիտարկչի աշխարհագրական տեղանքի թույլտվությունները"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Թույլ է տալիս հավելվածին փոփոխել զննարկչի աշխարհագրական դիրքի թույլտվությունները: Վնասարար հավելվածները կարող են օգտագործել սա` թույլատրելու ուղարկել տեղադրության վերաբերյալ տեղեկությունները կամայական վեբ կայքերին:"</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"հաստատել փաթեթները"</string>
@@ -1362,10 +1366,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Ծրագրին թույլ է տալիս լսել վստահության կարգավիճակի փոփոխությունները:"</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Տրամադրել վստահելի գործակալ:"</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Ծրագրին թույլ է տալիս տրամադրել վստահելի գործակալ:"</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Գործարկել վստահելի գործակալի կարգավորումների ցանկը:"</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Ծրագրին թույլ է տալիս իրականացնել այնպիսի գործունեություն, որը փոխում է վստահելի գործակալի վարքագիծը:"</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Կապվել վստահելի գործակալի ծառայությանը"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Ծրագրին թույլ է տալիս կապվել վստահելի գործակալի ծառայությանը:"</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Փոխազդել թարմացման և վերականգնման համակարգի հետ"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index 9dca8a5..1c80430 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Mengizinkan apl menyetel alarm di apl jam alarm yang terpasang. Beberapa apl jam alarm mungkin tidak menerapkan fitur ini."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"tambahkan kotak pesan"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Mengizinkan apl menambahkan pesan ke kotak masuk untuk pesan suara Anda."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"baca semua kotak pesan"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Mengizinkan aplikasi membaca semua kotak pesan Anda."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"memodifikasi izin geolokasi Browser"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Mengizinkan apl memodifikasi izin geolokasi Browser. Apl berbahaya dapat menggunakan izin ini untuk memungkinkan pengiriman informasi lokasi ke sembarang situs web."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifikasi paket"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 0ea6f6e..d7b7748 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Consente all\'applicazione di impostare un allarme in un\'applicazione sveglia installata. È possibile che alcune applicazioni sveglia non possano implementare questa funzione."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"aggiunta di un messaggio vocale"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Consente all\'applicazione di aggiungere messaggi alla casella della segreteria."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"accesso alla segreteria"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Consente all\'app di accedere alla segreteria."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modifica delle autorizzazioni di localizzazione geografica del browser"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Consente all\'applicazione di modificare le autorizzazioni di geolocalizzazione del Browser. Le applicazioni dannose potrebbero farne uso per consentire l\'invio di informazioni sulla posizione a siti web arbitrari."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verifica dei pacchetti"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 19fb05a..8b33339 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"מאפשר לאפליקציה להגדיר התראה באפליקציה מותקנת של שעון מעורר. אפליקציות מסוימות של שעון מעורר אינן מיישמות תכונה זו."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"הוסף דואר קולי"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"מאפשר לאפליקציה להוסיף הודעות לתיבת הדואר הקולי."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"קריאת כל הדואר הקולי"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"מאפשרת לאפליקציה לקרוא את כל הודעות הדואר הקולי שלך."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"שינוי הרשאות המיקום הגיאוגרפי של הדפדפן"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"מאפשר לאפליקציה לשנות את הרשאות המיקום הגיאוגרפי של הדפדפן. אפליקציות זדוניות עלולות להשתמש בכך כדי לאפשר משלוח של פרטי מיקום לאתרים זדוניים אחרים."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"אימות חבילות"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index 6ba99bf..a4b95a6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"インストール済みアラームアプリのアラームを設定することをアプリに許可します。この機能が実装されていないアラームアプリもあります。"</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"ボイスメールの追加"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"ボイスメール受信トレイにメッセージを追加することをアプリに許可します。"</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ブラウザの現在地情報に対する権限の変更"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ブラウザの現在地情報に対する権限の変更をアプリに許可します。この許可を悪意のあるアプリに利用されると、任意のウェブサイトに現在地情報が送信される恐れがあります。"</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"パッケージのベリファイ"</string>
diff --git a/core/res/res/values-ka-rGE/strings.xml b/core/res/res/values-ka-rGE/strings.xml
index 879d00f..694e1c7 100644
--- a/core/res/res/values-ka-rGE/strings.xml
+++ b/core/res/res/values-ka-rGE/strings.xml
@@ -401,7 +401,7 @@
     <string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"ხმის ინტერაქტორთან შეკავშირება"</string>
     <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"მფლობელს შეეძლება შეკავშირდეს ხმის ინტერაქციის სერვისების ზედა დონის ინტერფეისთან. ჩვეულებრივ აპს ეს წესით არასოდეს უნდა დასჭირდეს."</string>
     <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"ხმოვანი საიდუმლო ფრაზების მართვა"</string>
-    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"საშუალებას აძლევს მფლობელს მართოს საიდუმლო ფრაზები ხმოვანი ჯადოსნური სიტყვის ამოცნობისათვის. ეს ჩვეულებრივ აპებს არ უნდა დაჭირდეს."</string>
+    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"საშუალებას აძლევს მფლობელს მართოს საიდუმლო ფრაზები ხმოვანი ჯადოსნური სიტყვის ამოცნობისათვის. ეს ჩვეულებრივ აპებს არ უნდა დასჭირდეს."</string>
     <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"დისტანციურ მონიტორზე მიბმა"</string>
     <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"მფლობელს შეეძლება მიებას დისტანციურ მონიტორის ზედა დონის ინტერფეისს. ჩვეულებრივ აპს ეს წესით არასოდეს უნდა დაჭირდეს."</string>
     <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ვიჯეტ სერვისთან დაკავშირება"</string>
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"აპს შეეძლება მაღვიძარას დაყენება დაინსტალირებული მაღვიძარას აპლიკაციაში. ამ ფუნქციას მაღვიძარას ზოგიერთი აპი არ იყენებს."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"ხმოვანი ფოსტის დამატება"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"აპს შეეძლება დაამატოს შეტყობინებები თქვენი ხმოვანი ფოსტის შემოსულებში."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ბრაუზერის გეოლოკაციის უფლებების შეცვლა"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"აპს შეეძლება ბრაუზერის გეოლოკაციის უფლებების შეცვლა. მავნე აპებმა ეს შესაძლოა გამოიყენონ  ნებისმიერი ვებსაიტისთვის მდებარეობის შესახებ ინფორმაციის გასაგზავნად."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"პაკეტების გადამოწმება"</string>
@@ -1362,10 +1366,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"საშუალებას აძლევს აპლიკაციას მოუსმინოს ცვლილებებს სანდო მდგომარეობაში."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"სანდო აგენტის წარმოდგენა."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"საშუალებას აძლევს აპლიკაციას წარმოადგინოს სანდო აგენტი."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"ნდობის აგენტის პარამეტრების მენიუს გამოძახება."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"საშუალებას აძლევს აპლიკაციას გამოიძახოს აქტივობა, რაც ნდობის აგენტის ქცევას ცვლის."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"სანდო აგენტის სერვისზე მიმაგრება."</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"საშუალებას აძლევს აპლიკაციას მიემაგროს სანდო აგენტის სერვისს."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"განახლებასთან და აღდგენის სისტემასთან ინტერაქცია"</string>
diff --git a/core/res/res/values-km-rKH/strings.xml b/core/res/res/values-km-rKH/strings.xml
index daf004c..421ff10 100644
--- a/core/res/res/values-km-rKH/strings.xml
+++ b/core/res/res/values-km-rKH/strings.xml
@@ -68,7 +68,7 @@
   </plurals>
     <string name="imei" msgid="2625429890869005782">"IMEI"</string>
     <string name="meid" msgid="4841221237681254195">"MEID"</string>
-    <string name="ClipMmi" msgid="6952821216480289285">"លេខ​សម្គាល់​អ្នក​ហៅ​​ចូល​"</string>
+    <string name="ClipMmi" msgid="6952821216480289285">"លេខ​សម្គាល់​អ្នក​ហៅ​​ចូល"</string>
     <string name="ClirMmi" msgid="7784673673446833091">"លេខ​សម្គាល់​អ្នក​ហៅ​ចេញ"</string>
     <string name="CfMmi" msgid="5123218989141573515">"បញ្ជូន​ការ​ហៅ​បន្ត"</string>
     <string name="CwMmi" msgid="9129678056795016867">"រង់ចាំ​ការ​ហៅ"</string>
@@ -125,7 +125,7 @@
     <string name="cfTemplateRegisteredTime" msgid="6781621964320635172">"<xliff:g id="BEARER_SERVICE_CODE">{0}</xliff:g> ៖ មិន​បាន​បញ្ជូន​បន្ត"</string>
     <string name="fcComplete" msgid="3118848230966886575">"កូដ​លក្ខណៈ​ពេញលេញ។"</string>
     <string name="fcError" msgid="3327560126588500777">"បញ្ហា​ការ​តភ្ជាប់​ ឬ​កូដ​លក្ខណៈ​​​មិន​ត្រឹមត្រូវ​។"</string>
-    <string name="httpErrorOk" msgid="1191919378083472204">"យល់​ព្រម​"</string>
+    <string name="httpErrorOk" msgid="1191919378083472204">"យល់​ព្រម"</string>
     <string name="httpError" msgid="7956392511146698522">"មាន​កំហុស​បណ្ដាញ។"</string>
     <string name="httpErrorLookup" msgid="4711687456111963163">"រក​មិន​ឃើញ URL ។"</string>
     <string name="httpErrorUnsupportedAuthScheme" msgid="6299980280442076799">"គ្រោងការណ៍​ផ្ទៀងផ្ទាត់​តំបន់បណ្ដាញ​មិន​ត្រូវ​បាន​គាំទ្រ។"</string>
@@ -183,7 +183,7 @@
     <string name="global_action_silent_mode_off_status" msgid="1506046579177066419">"បើក​សំឡេង"</string>
     <string name="global_actions_toggle_airplane_mode" msgid="5884330306926307456">"ពេល​ជិះ​យន្តហោះ"</string>
     <string name="global_actions_airplane_mode_on_status" msgid="2719557982608919750">"បាន​បើក​របៀប​ពេល​ជិះ​យន្ត​ហោះ"</string>
-    <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បាន​បិទ​របៀបពេលជិះ​យន្តហោះ​"</string>
+    <string name="global_actions_airplane_mode_off_status" msgid="5075070442854490296">"បាន​បិទ​របៀបពេលជិះ​យន្តហោះ"</string>
     <string name="global_action_settings" msgid="1756531602592545966">"ការ​កំណត់"</string>
     <string name="status_bar_notification_info_overflow" msgid="5301981741705354993">"999+"</string>
     <string name="safeMode" msgid="2788228061547930246">"របៀប​​​សុវត្ថិភាព"</string>
@@ -195,7 +195,7 @@
     <string name="permgrouplab_messages" msgid="7521249148445456662">"សារ​របស់​អ្នក"</string>
     <string name="permgroupdesc_messages" msgid="7821999071003699236">"អាន និង​សរសេរ​សារ SMS, អ៊ីមែល និង​សារ​ផ្សេងៗ​ទៀត​របស់​អ្នក។"</string>
     <string name="permgrouplab_personalInfo" msgid="3519163141070533474">"ព័ត៌មាន​ផ្ទាល់ខ្លួន​របស់​អ្នក"</string>
-    <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​អ្នក​ ដែល​បា​ន​រក្សាទុក​ក្នុង​កាត​ទំនាក់ទំនង​របស់​អ្នក។​"</string>
+    <string name="permgroupdesc_personalInfo" msgid="8426453129788861338">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​អ្នក​ ដែល​បា​ន​រក្សាទុក​ក្នុង​កាត​ទំនាក់ទំនង​របស់​អ្នក។"</string>
     <string name="permgrouplab_socialInfo" msgid="5799096623412043791">"ព័ត៌មាន​សង្គម​របស់​អ្នក"</string>
     <string name="permgroupdesc_socialInfo" msgid="7129842457611643493">"ចូល​ដំណើរការ​ព័ត៌មាន​ដោយ​ផ្ទាល់​អំពី​ទំនាក់ទំនង និង​ការ​ភ្ជាប់​សង្គម​របស់​អ្នក។"</string>
     <string name="permgrouplab_location" msgid="635149742436692049">"ទីតាំង​របស់​អ្នក"</string>
@@ -384,7 +384,7 @@
     <string name="permdesc_readInputState" msgid="8387754901688728043">"ឲ្យ​កម្មវិធី​មើល​គ្រាប់​ចុច​ដែល​អ្នក​ចុច​ពេល​មាន​អន្តរកម្ម​ជា​មួយ​កម្មវិធី​ផ្សេង (ដូចជា បញ្ចូល​ពាក្យ​សម្ងាត់)។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindInputMethod" msgid="3360064620230515776">"ចង​ទៅ​វិធីសាស្ត្រ​បញ្ចូល"</string>
     <string name="permdesc_bindInputMethod" msgid="3250440322807286331">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​វិធី​សាស្ត្រ​បញ្ចូល។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចង​សេវា​កម្ម​ភាព​មធ្យោបាយ​ងាយស្រួល​"</string>
+    <string name="permlab_bindAccessibilityService" msgid="5357733942556031593">"ចង​សេវា​កម្ម​ភាព​មធ្យោបាយ​ងាយស្រួល"</string>
     <string name="permdesc_bindAccessibilityService" msgid="7034615928609331368">"ឲ្យ​​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ភាព​ងាយស្រួល។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindPrintService" msgid="8462815179572748761">"ចង​សេវាកម្ម​​បោះពុម្ព"</string>
     <string name="permdesc_bindPrintService" msgid="7960067623209111135">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ធាតុ​ក្រាហ្វិក។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -404,7 +404,7 @@
     <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​គ្រប់គ្រង​ឃ្លា​​សម្រាប់​​ការ​រក​ឃើញ​​​ពាក្យ​​ជា​សំឡេង។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ភ្ជាប់​ទៅ​ការ​បង្ហាញ​ពី​ចម្ងាយ"</string>
     <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​​ទៅ​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​ការ​បង្ហាញ​ពី​ចម្ងាយ។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចង​សេវា​កម្ម​ធាតុ​ក្រាហ្វិក​"</string>
+    <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"ចង​សេវា​កម្ម​ធាតុ​ក្រាហ្វិក"</string>
     <string name="permdesc_bindRemoteViews" msgid="4717987810137692572">"ឲ្យ​ម្ចាស់​ចង​ចំណុច​ប្រទាក់​កម្រិត​កំពូល​នៃ​សេវាកម្ម​ធាតុ​ក្រាហ្វិក។ មិន​គួរ​​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindRouteProvider" msgid="4869394607915096847">"ភ្ជាប់​ទៅ​សេវាកម្ម​ក្រុមហ៊ុន​ផ្ដល់​ច្រក"</string>
     <string name="permdesc_bindRouteProvider" msgid="4703804520859960329">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​ទៅ​ក្រុមហ៊ុន​ផ្ដល់​​ច្រក​ដែល​បាន​ចុះ​ឈ្មោះ។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -412,7 +412,7 @@
     <string name="permdesc_bindDeviceAdmin" msgid="569715419543907930">"ឲ្យ​ម្ចាស់​ផ្ញើ​គោលបំណង​​ទៅ​អ្នក​គ្រប់គ្រង​ឧបករណ៍។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
     <string name="permlab_bindTvInput" msgid="5601264742478168987">"ភ្ជាប់​ទៅ​ការ​បញ្ចូល​ទូរទស្សន៍"</string>
     <string name="permdesc_bindTvInput" msgid="2371008331852001924">"អនុញ្ញាត​ឲ្យ​ម្ចាស់​ភ្ជាប់​ទៅ​ចំណុចប្រទាក់​កម្រិត​ខ្ពស់​នៃ​ការ​បញ្ចូល​ទូរទស្សន៍។ មិន​គួរ​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
-    <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​​​ឧបករណ៍​​"</string>
+    <string name="permlab_manageDeviceAdmins" msgid="4248828900045808722">"បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​​​ឧបករណ៍"</string>
     <string name="permdesc_manageDeviceAdmins" msgid="5025608167709942485">"អនុញ្ញាត​​​ឲ្យ​ម្ចាស់​​​បន្ថែម​ ឬ​លុប​កម្មវិធី​គ្រប់គ្រង​ឧបករណ៍​សកម្ម​ចេញ​។ មិន​គួរ​ប្រើ​សម្រាប់​កម្មវិធី​​ធម្មតា​ទេ​។"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"ប្ដូរ​ទិស​អេក្រង់"</string>
     <string name="permdesc_setOrientation" msgid="3046126619316671476">"ឲ្យ​កម្មវិធី​ប្ដូរ​ការ​បង្វិល​អេក្រង់​នៅ​ពេល​ណា​មួយ។ មិន​ចាំបាច់​សម្រាប់​កម្មវិធី​ធម្មតា​ទេ។"</string>
@@ -424,9 +424,9 @@
     <string name="permdesc_signalPersistentProcesses" msgid="4896992079182649141">"ឲ្យ​កម្មវិធី​ស្នើ​​សញ្ញា​ដែល​បាន​ផ្ដល់​ត្រូវ​ផ្ញើ​ទៅ​ដំណើរការ​ស្ថិតស្ថេរ​​ទាំង​អស់។"</string>
     <string name="permlab_persistentActivity" msgid="8841113627955563938">"ធ្វើ​ឲ្យ​កម្មវិធី​ដំណើរការ​ជា​និច្ច"</string>
     <string name="permdesc_persistentActivity" product="tablet" msgid="8525189272329086137">"ឲ្យ​កម្មវិធី​ធ្វើជា​ផ្នែក​​ស្ថិតស្ថេរ​ដោយ​ខ្លួន​ឯង​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កំណត់​អង្គ​ចងចាំ​ដែល​អាច​ប្រើ​បាន​ចំពោះ​កម្មវិធី​ផ្សេងៗ​ ដោយ​ធ្វើឲ្យ​កុំព្យូទ័រ​បន្ទះ​យឺត។"</string>
-    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យ​កម្មវិធី ធ្វើជា​ផ្នែក​អចិន្ត្រៃយ៍​នៃ​ខ្លួន​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កម្រិត​អង្គ​ចងចាំ​អាច​ប្រើ​បាន​ ដើម្បី​ធ្វើ​ឲ្យ​កម្មវិធី​ផ្សេង​ធ្វើ​ឲ្យ​ទូរស័ព្ទ​របស់​អ្នក​យឺត។​"</string>
+    <string name="permdesc_persistentActivity" product="default" msgid="4384760047508278272">"ឲ្យ​កម្មវិធី ធ្វើជា​ផ្នែក​អចិន្ត្រៃយ៍​នៃ​ខ្លួន​ក្នុង​អង្គ​ចងចាំ។ វា​អាច​កម្រិត​អង្គ​ចងចាំ​អាច​ប្រើ​បាន​ ដើម្បី​ធ្វើ​ឲ្យ​កម្មវិធី​ផ្សេង​ធ្វើ​ឲ្យ​ទូរស័ព្ទ​របស់​អ្នក​យឺត។"</string>
     <string name="permlab_deletePackages" msgid="184385129537705938">"លុប​កម្មវិធី"</string>
-    <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យ​កម្មវិធី​លុប​កញ្ចប់ Android ។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប​កម្មវិធី​សំខាន់​ៗ។ ​"</string>
+    <string name="permdesc_deletePackages" msgid="7411480275167205081">"ឲ្យ​កម្មវិធី​លុប​កញ្ចប់ Android ។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប​កម្មវិធី​សំខាន់​ៗ។"</string>
     <string name="permlab_clearAppUserData" msgid="274109191845842756">"លុប​ទិន្នន័យ​របស់​​កម្មវិធី​ផ្សេង"</string>
     <string name="permdesc_clearAppUserData" msgid="4625323684125459488">"ឲ្យ​កម្មវិធី​សម្អាត​ទិន្នន័យ​អ្នក​ប្រើ។"</string>
     <string name="permlab_deleteCacheFiles" msgid="3128665571837408675">"លុប​ឃ្លាំង​សម្ងាត់​កម្មវិធី​ផ្សេងៗ"</string>
@@ -477,7 +477,7 @@
     <string name="permdesc_writeContacts" product="tablet" msgid="897243932521953602">"ឲ្យ​កម្មវិធី​កែ​ទិន្នន័យ​អំពី​ទំនាក់ទំនង​របស់​អ្នក​ដែល​បាន​រក្សាទុក​ក្នុង​កុំព្យូទ័រ​បន្ទះ រួមមាន​ប្រេកង់​​ដែល​អ្នក​បាន​ហៅ អ៊ីមែល ឬ​ទាក់ទង​តាម​វិធី​ផ្សេងៗ​ជា​មួយ​ទំនាក់ទំនង​ជាក់លាក់។ សិទ្ធិ​​នេះ​អនុញ្ញាត​ឲ្យ​​​កម្មវិធី​លុប​ទិន្នន័យ​ទំនាក់ទំនង​របស់​អ្នក។"</string>
     <string name="permdesc_writeContacts" product="default" msgid="589869224625163558">"ឲ្យ​កម្មវិធី​កែ​ទិន្នន័យ​អំពី​ទំនាក់ទំនង​របស់​អ្នក​ដែល​បាន​រក្សាទុក​ក្នុង​ទូរស័ព្ទ​របស់​អ្នក រួមមាន​ប្រេកង់​ដែល​អ្នក​បាន​ហៅ អ៊ីមែល ឬ​បាន​ទាក់ទង​​តាម​វិធី​ផ្សេងៗ​ជា​មួយ​ទំនាក់​ទំនាក់​ជាក់លាក់។ សិទ្ធិ​នេះ​ឲ្យ​កម្មវិធី​លុប​ទិន្នន័យ​ទំនាក់ទំនង។"</string>
     <string name="permlab_readCallLog" msgid="3478133184624102739">"អាន​​កំណត់​ហេតុ​​​ហៅ"</string>
-    <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យ​កម្មវិធី​អាន​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នក​ដឹង។​"</string>
+    <string name="permdesc_readCallLog" product="tablet" msgid="3700645184870760285">"ឲ្យ​កម្មវិធី​អាន​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នក​ដឹង។"</string>
     <string name="permdesc_readCallLog" product="default" msgid="5777725796813217244">"ឲ្យ​កម្មវិធី​អាន​​​បញ្ជី​ហៅ​ទូរស័ព្ទ​របស់​អ្នក រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។ សិទ្ធិ​នេះ​អនុញ្ញាត​ឲ្យ​កម្មវិធី​រក្សាទុក​ទិន្នន័យ​បញ្ជី​ហៅ​របស់​អ្នក ហើយ​កម្មវិធី​ព្យាបាទ​អាច​ចែករំលែក​ទិន្នន័យ​បញ្ជី​ហៅ​ដោយ​មិន​ឲ្យ​អ្នកដឹង។"</string>
     <string name="permlab_writeCallLog" msgid="8552045664743499354">"សរសេរ​បញ្ជី​ហៅ"</string>
     <string name="permdesc_writeCallLog" product="tablet" msgid="6661806062274119245">"ឲ្យ​កម្មវិធី​កែ​បញ្ជី​ហៅ​កុំព្យូទ័រ​បន្ទះ​របស់​អ្នក​រួមមាន​ទិន្នន័យ​អំពី​ការ​ហៅ​ចូល និង​ចេញ។​កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​លុប ឬ​កែ​បញ្ជី​ហៅ​របស់​អ្នក។"</string>
@@ -613,7 +613,7 @@
     <string name="permdesc_setWallpaperHints" msgid="8235784384223730091">"ឲ្យ​កម្មវិធី​កំណត់​ជំនួយ​ទំហំ​ផ្ទាំង​រូបភាព​ប្រព័ន្ធ។"</string>
     <string name="permlab_masterClear" msgid="2315750423139697397">"កំណត់​ប្រព័ន្ធ​ទៅ​លំនាំដើម​រោងចក្រ​ឡើងវិញ"</string>
     <string name="permdesc_masterClear" msgid="3665380492633910226">"ឲ្យ​កម្មវិធី​កំណត់​ប្រព័ន្ធ​​ដូច​ការ​កំណត់​ចេញ​ពី​រោងចក្រ​ឡើងវិញ​ពេញលេញ ដោយ​លុប​ទិន្នន័យ ការ​កំណត់​រចនាសម្ព័ន្ធ និង​កម្មវិធី​បាន​ដំឡើង។"</string>
-    <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់​​ម៉ោង​"</string>
+    <string name="permlab_setTime" msgid="2021614829591775646">"កំណត់​​ម៉ោង"</string>
     <string name="permdesc_setTime" product="tablet" msgid="1896341438151152881">"ឲ្យ​កម្មវិធី​ប្ដូរ​ម៉ោង​កុំព្យូទ័រ​បន្ទះ។"</string>
     <string name="permdesc_setTime" product="default" msgid="1855702730738020">"ឲ្យ​កម្មវិធី​ប្ដូរ​ម៉ោង​ទូរស័ព្ទ។"</string>
     <string name="permlab_setTimeZone" msgid="2945079801013077340">"កំណត់​តំបន់​ពេលវេលា"</string>
@@ -779,7 +779,7 @@
   <string-array name="organizationTypes">
     <item msgid="7546335612189115615">"កន្លែង​ធ្វើការ"</item>
     <item msgid="4378074129049520373">"ផ្សេងៗ"</item>
-    <item msgid="3455047468583965104">"តាម​តម្រូវ​ការ​"</item>
+    <item msgid="3455047468583965104">"តាម​តម្រូវ​ការ"</item>
   </string-array>
   <string-array name="imProtocols">
     <item msgid="8595261363518459565">"AIM"</item>
@@ -795,7 +795,7 @@
     <string name="phoneTypeHome" msgid="2570923463033985887">"ផ្ទះ"</string>
     <string name="phoneTypeMobile" msgid="6501463557754751037">"​ចល័ត"</string>
     <string name="phoneTypeWork" msgid="8863939667059911633">"កន្លែង​ធ្វើការ"</string>
-    <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារ​កន្លែង​ធ្វើការ​"</string>
+    <string name="phoneTypeFaxWork" msgid="3517792160008890912">"ទូរសារ​កន្លែង​ធ្វើការ"</string>
     <string name="phoneTypeFaxHome" msgid="2067265972322971467">"ទូរសារ​ផ្ទះ"</string>
     <string name="phoneTypePager" msgid="7582359955394921732">"ភេយ័រ"</string>
     <string name="phoneTypeOther" msgid="1544425847868765990">"ផ្សេងៗ"</string>
@@ -920,7 +920,7 @@
     <string name="lockscreen_glogin_too_many_attempts" msgid="2751368605287288808">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="lockscreen_glogin_instructions" msgid="3931816256100707784">"ដើម្បី​ដោះ​សោ ចូល​គណនី Google របស់​អ្នក។"</string>
     <string name="lockscreen_glogin_username_hint" msgid="8846881424106484447">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ីមែល​)"</string>
-    <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់​"</string>
+    <string name="lockscreen_glogin_password_hint" msgid="5958028383954738528">"ពាក្យសម្ងាត់"</string>
     <string name="lockscreen_glogin_submit_button" msgid="7130893694795786300">"ចូល"</string>
     <string name="lockscreen_glogin_invalid_input" msgid="1364051473347485908">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="lockscreen_glogin_account_recovery_hint" msgid="1696924763690379073">"ភ្លេច​ឈ្មោះ​អ្នក​ប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -965,7 +965,7 @@
     <string name="factorytest_failed" msgid="5410270329114212041">"បាន​បរាជ័យ​ក្នុង​ការ​សាកល្បង​រោងចក្រ"</string>
     <string name="factorytest_not_system" msgid="4435201656767276723">"សកម្មភាព FACTORY_TEST ត្រូវ​បាន​គាំទ្រ​សម្រាប់​តែ​កញ្ចប់​បាន​ដំឡើង​ក្នុង /system/app."</string>
     <string name="factorytest_no_action" msgid="872991874799998561">"រក​មិន​ឃើញ​កញ្ចប់​ដែល​ផ្ដល់​សកម្មភាព FACTORY_TEST ។"</string>
-    <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់​ផ្ដើម​ឡើង​វិញ​"</string>
+    <string name="factorytest_reboot" msgid="6320168203050791643">"ចាប់​ផ្ដើម​ឡើង​វិញ"</string>
     <string name="js_dialog_title" msgid="1987483977834603872">"ទំព័រ​មាន​ចំណងជើង \"<xliff:g id="TITLE">%s</xliff:g>\" សរសេរ៖"</string>
     <string name="js_dialog_title_default" msgid="6961903213729667573">"JavaScript"</string>
     <string name="js_dialog_before_unload_title" msgid="2619376555525116593">"បញ្ជាក់​ការ​រុករក"</string>
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"ឲ្យ​កម្មវិធី​កំណត់​​សំឡេង​រោទ៍​ក្នុង​កម្មវិធី​នាឡិកា​រោទ៍​បាន​ដំឡើង។​ កម្មវិធី​នាឡិកា​រោទ៍​មួយ​ចំនួន​អាច​មិន​អនុវត្ត​លក្ខណៈ​នេះ។"</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"បន្ថែម​សារ​ជា​សំឡេង"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"ឲ្យ​កម្មវិធី​បន្ថែម​សារ​ទៅ​ប្រអប់​ទទួល​សារ​ជា​សំឡេង​របស់​អ្នក។"</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"កែ​សិទ្ធិ​ទីតាំង​ភូមិសាស្ត្រ​របស់​​កម្មវិធី​អ៊ីនធឺណិត"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ឲ្យ​កម្មវិធី​កែ​​សិទ្ធិ​ទី​តាំង​ភូមិសាស្ត្រ​របស់​កម្មវិធី​អ៊ីនធឺណិត។ កម្មវិធី​ព្យាបាទ​អាច​ប្រើ​វា ដើម្បី​ឲ្យ​ផ្ញើ​ព័ត៌មាន​ទីតាំង​ទៅ​តំបន់បណ្ដាញ​ដោយ​បំពាន។"</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ផ្ទៀងផ្ទាត់​កញ្ចប់"</string>
@@ -1023,7 +1027,7 @@
     <string name="prepend_shortcut_label" msgid="2572214461676015642">"ម៉ឺនុយ +"</string>
     <string name="menu_space_shortcut_label" msgid="2410328639272162537">"ដកឃ្លា"</string>
     <string name="menu_enter_shortcut_label" msgid="2743362785111309668">"enter"</string>
-    <string name="menu_delete_shortcut_label" msgid="3658178007202748164">"លុប​"</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="searchview_description_query" msgid="5911778593125355124">"ស្វែងរក​សំណួរ"</string>
@@ -1107,18 +1111,18 @@
     <string name="preposition_for_date" msgid="9093949757757445117">"នៅ <xliff:g id="DATE">%s</xliff:g>"</string>
     <string name="preposition_for_time" msgid="5506831244263083793">"នៅ​ម៉ោង <xliff:g id="TIME">%s</xliff:g>"</string>
     <string name="preposition_for_year" msgid="5040395640711867177">"ក្នុង​ឆ្នាំ <xliff:g id="YEAR">%s</xliff:g>"</string>
-    <string name="day" msgid="8144195776058119424">"ថ្ងៃ​"</string>
+    <string name="day" msgid="8144195776058119424">"ថ្ងៃ"</string>
     <string name="days" msgid="4774547661021344602">"​ថ្ងៃ"</string>
     <string name="hour" msgid="2126771916426189481">"ម៉ោង"</string>
     <string name="hours" msgid="894424005266852993">"ម៉ោង"</string>
-    <string name="minute" msgid="9148878657703769868">"នាទី​"</string>
+    <string name="minute" msgid="9148878657703769868">"នាទី"</string>
     <string name="minutes" msgid="5646001005827034509">"នាទី"</string>
-    <string name="second" msgid="3184235808021478">"វិនាទី​"</string>
+    <string name="second" msgid="3184235808021478">"វិនាទី"</string>
     <string name="seconds" msgid="3161515347216589235">"វិនាទី"</string>
-    <string name="week" msgid="5617961537173061583">"សប្ដាហ៍​"</string>
-    <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍​"</string>
-    <string name="year" msgid="4001118221013892076">"ឆ្នាំ​"</string>
-    <string name="years" msgid="6881577717993213522">"ឆ្នាំ​"</string>
+    <string name="week" msgid="5617961537173061583">"សប្ដាហ៍"</string>
+    <string name="weeks" msgid="6509623834583944518">"សប្ដាហ៍"</string>
+    <string name="year" msgid="4001118221013892076">"ឆ្នាំ"</string>
+    <string name="years" msgid="6881577717993213522">"ឆ្នាំ"</string>
   <plurals name="duration_seconds">
     <item quantity="one" msgid="6962015528372969481">"1 វិនាទី"</item>
     <item quantity="other" msgid="1886107766577166786">"<xliff:g id="COUNT">%d</xliff:g> វិនាទី"</item>
@@ -1134,12 +1138,12 @@
     <string name="VideoView_error_title" msgid="3534509135438353077">"បញ្ហា​វីដេអូ"</string>
     <string name="VideoView_error_text_invalid_progressive_playback" msgid="3186670335938670444">"វីដេអូ​នេះ​មិន​ត្រឹមត្រូវ​សម្រាប់​​ចរន្ត​ចូល​ឧបករណ៍​នេះ។"</string>
     <string name="VideoView_error_text_unknown" msgid="3450439155187810085">"មិន​អាច​ចាក់​វីដេអូ​នេះ។"</string>
-    <string name="VideoView_error_button" msgid="2822238215100679592">"យល់​ព្រម​"</string>
+    <string name="VideoView_error_button" msgid="2822238215100679592">"យល់​ព្រម"</string>
     <string name="relative_time" msgid="1818557177829411417">"<xliff:g id="DATE">%1$s</xliff:g>, <xliff:g id="TIME">%2$s</xliff:g>"</string>
     <string name="noon" msgid="7245353528818587908">"រសៀល"</string>
     <string name="Noon" msgid="3342127745230013127">"រសៀល"</string>
     <string name="midnight" msgid="7166259508850457595">"កណ្ដាលអធ្រាត្រ"</string>
-    <string name="Midnight" msgid="5630806906897892201">"កណ្ដាល​អធ្រាត្រ​"</string>
+    <string name="Midnight" msgid="5630806906897892201">"កណ្ដាល​អធ្រាត្រ"</string>
     <string name="elapsed_time_short_format_mm_ss" msgid="4431555943828711473">"<xliff:g id="MINUTES">%1$02d</xliff:g>:<xliff:g id="SECONDS">%2$02d</xliff:g>"</string>
     <string name="elapsed_time_short_format_h_mm_ss" msgid="1846071997616654124">"<xliff:g id="HOURS">%1$d</xliff:g>:<xliff:g id="MINUTES">%2$02d</xliff:g>:<xliff:g id="SECONDS">%3$02d</xliff:g>"</string>
     <string name="selectAll" msgid="6876518925844129331">"ជ្រើស​ទាំងអស់"</string>
@@ -1156,13 +1160,13 @@
     <string name="inputMethod" msgid="1653630062304567879">"វិធីសាស្ត្រ​បញ្ចូល"</string>
     <string name="editTextMenuTitle" msgid="4909135564941815494">"សកម្មភាព​អត្ថបទ"</string>
     <string name="low_internal_storage_view_title" msgid="5576272496365684834">"អស់​ទំហំ​ផ្ទុក"</string>
-    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ​"</string>
+    <string name="low_internal_storage_view_text" msgid="6640505817617414371">"មុខងារ​ប្រព័ន្ធ​មួយ​ចំនួន​អាច​មិន​ដំណើរការ"</string>
     <string name="app_running_notification_title" msgid="8718335121060787914">"<xliff:g id="APP_NAME">%1$s</xliff:g> កំពុង​ដំណើរការ"</string>
     <string name="app_running_notification_text" msgid="4653586947747330058">"ប៉ះ​ ដើម្បី​មើល​ព័ត៌មាន​បន្ថែម ឬ​បញ្ឈប់​កម្មវិធី។"</string>
-    <string name="ok" msgid="5970060430562524910">"យល់​ព្រម​"</string>
-    <string name="cancel" msgid="6442560571259935130">"បោះ​បង់​"</string>
-    <string name="yes" msgid="5362982303337969312">"យល់​ព្រម​"</string>
-    <string name="no" msgid="5141531044935541497">"បោះ​បង់​"</string>
+    <string name="ok" msgid="5970060430562524910">"យល់​ព្រម"</string>
+    <string name="cancel" msgid="6442560571259935130">"បោះ​បង់"</string>
+    <string name="yes" msgid="5362982303337969312">"យល់​ព្រម"</string>
+    <string name="no" msgid="5141531044935541497">"បោះ​បង់"</string>
     <string name="dialog_alert_title" msgid="2049658708609043103">"ប្រយ័ត្ន"</string>
     <string name="loading" msgid="7933681260296021180">"កំពុង​ផ្ទុក..."</string>
     <string name="capital_on" msgid="1544682755514494298">"បើក"</string>
@@ -1171,7 +1175,7 @@
     <string name="whichHomeApplication" msgid="4616420172727326782">"ជ្រើស​កម្មវិធី​ដើម"</string>
     <string name="alwaysUse" msgid="4583018368000610438">"ប្រើ​តាម​លំនាំដើម​សម្រាប់​សកម្មភាព​នេះ។"</string>
     <string name="clearDefaultHintMsg" msgid="3252584689512077257">"សម្អាត​លំនាំដើម​ក្នុង​ការកំណត់​ប្រព័ន្ធ &gt; កម្មវិធី &gt; ទាញ​យក។"</string>
-    <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើស​សកម្មភាព​​"</string>
+    <string name="chooseActivity" msgid="7486876147751803333">"ជ្រើស​សកម្មភាព"</string>
     <string name="chooseUsbActivity" msgid="6894748416073583509">"ជ្រើស​កម្មវិធី​សម្រាប់​ឧបករណ៍​យូអេសប៊ី"</string>
     <string name="noApplications" msgid="2991814273936504689">"គ្មាន​កម្មវិធី​អាច​អនុវត្ត​សកម្មភាព​នេះ។"</string>
     <string name="aerr_title" msgid="1905800560317137752"></string>
@@ -1182,7 +1186,7 @@
     <string name="anr_activity_process" msgid="5776209883299089767">"សកម្មភាព <xliff:g id="ACTIVITY">%1$s</xliff:g> មិន​ឆ្លើយតប។\n\nតើ​អ្នក​ចង់​បិទ​វា?"</string>
     <string name="anr_application_process" msgid="8941757607340481057">"<xliff:g id="APPLICATION">%1$s</xliff:g> មិន​ឆ្លើយតប។ តើ​អ្នក​ចង់​បិទ​វា?"</string>
     <string name="anr_process" msgid="6513209874880517125">"ដំណើរការ <xliff:g id="PROCESS">%1$s</xliff:g> មិន​ឆ្លើយតប។ \n\nតើ​អ្នក​ចង់​បិទ​វា​ឬ?"</string>
-    <string name="force_close" msgid="8346072094521265605">"យល់​ព្រម​"</string>
+    <string name="force_close" msgid="8346072094521265605">"យល់​ព្រម"</string>
     <string name="report" msgid="4060218260984795706">"រាយការណ៍"</string>
     <string name="wait" msgid="7147118217226317732">"រង់ចាំ"</string>
     <string name="webpage_unresponsive" msgid="3272758351138122503">"ទំព័រ​ក្លាយ​ជា​មិន​ឆ្លើយតប។\n\nតើ​អ្នក​​ចង់​បិទ​វា?"</string>
@@ -1264,19 +1268,19 @@
     <string name="sms_short_code_details" msgid="3492025719868078457"><font fgcolor="#ffffb060">"នេះ​អាច​កាត់​លុយ"</font>" លើ​គណនី​ចល័ត​របស់​អ្នក។"</string>
     <string name="sms_premium_short_code_details" msgid="5523826349105123687"><font fgcolor="#ffffb060">"វា​នឹង​គិត​ថ្លៃ​សេវាកម្ម​លើ​គណនី​ចល័ត​របស់​អ្នក។"</font></string>
     <string name="sms_short_code_confirm_allow" msgid="4458878637111023413">"ផ្ញើ"</string>
-    <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះ​បង់​"</string>
+    <string name="sms_short_code_confirm_deny" msgid="2927389840209170706">"បោះ​បង់"</string>
     <string name="sms_short_code_remember_choice" msgid="5289538592272218136">"ចងចាំ​ជម្រើស​របស់​ខ្ញុំ"</string>
     <string name="sms_short_code_remember_undo_instruction" msgid="4960944133052287484">"អ្នក​អាច​ប្ដូរ​វា​ពេល​ក្រោយ​ក្នុង​ការ​កំណត់ &gt; កម្មវិធី"</string>
     <string name="sms_short_code_confirm_always_allow" msgid="3241181154869493368">"អនុញ្ញាត​ជា​និច្ច"</string>
     <string name="sms_short_code_confirm_never_allow" msgid="446992765774269673">"កុំ​អនុញ្ញាត"</string>
     <string name="sim_removed_title" msgid="6227712319223226185">"បាន​ដក​ស៊ីម​កាត​ចេញ"</string>
-    <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញ​ចល័ត​នឹង​ប្រើ​លែង​បាន​រហូត​ដល់​អ្នក​ចាប់ផ្ដើម​ជា​មួយ​ស៊ីម​កាត​ដែល​បា​បញ្ចូល​ត្រឹមត្រូវ។​"</string>
+    <string name="sim_removed_message" msgid="2333164559970958645">"បណ្ដាញ​ចល័ត​នឹង​ប្រើ​លែង​បាន​រហូត​ដល់​អ្នក​ចាប់ផ្ដើម​ជា​មួយ​ស៊ីម​កាត​ដែល​បា​បញ្ចូល​ត្រឹមត្រូវ។"</string>
     <string name="sim_done_button" msgid="827949989369963775">"រួចរាល់"</string>
     <string name="sim_added_title" msgid="3719670512889674693">"បាន​បន្ថែម​ស៊ីម​កាត"</string>
     <string name="sim_added_message" msgid="6599945301141050216">"ចាប់ផ្ដើម​ឧបករណ៍​របស់​អ្នក​ឡើង​វិញ ដើម្បី​ចូល​ដំណើរការ​បណ្ដាញ​ចល័ត។"</string>
     <string name="sim_restart_button" msgid="4722407842815232347">"ចាប់ផ្ដើម​ឡើងវិញ"</string>
-    <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់​ម៉ោង​"</string>
-    <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់​កាល​បរិច្ឆេទ​"</string>
+    <string name="time_picker_dialog_title" msgid="8349362623068819295">"កំណត់​ម៉ោង"</string>
+    <string name="date_picker_dialog_title" msgid="5879450659453782278">"កំណត់​កាល​បរិច្ឆេទ"</string>
     <string name="date_time_set" msgid="5777075614321087758">"កំណត់"</string>
     <string name="date_time_done" msgid="2507683751759308828">"រួចរាល់"</string>
     <string name="perms_new_perm_prefix" msgid="8257740710754301407"><font size="12" fgcolor="#ff33b5e5">"ថ្មី៖ "</font></string>
@@ -1354,7 +1358,7 @@
     <string name="permdesc_copyProtectedData" msgid="4390697124288317831">"ឲ្យ​កម្មវិធី​ដក​សេវាកម្ម​នៃ​កម្មវិធី​ផ្ទុក​​លំនាំដើម ដើម្បី​ចម្លង​មាតិកា។​ មិន​សម្រាប់​ប្រើ​ដោយ​កម្មវិធី​លំនាំដើម។"</string>
     <string name="permlab_route_media_output" msgid="1642024455750414694">"នាំ​ផ្លូវ​លទ្ធផល​មេឌៀ"</string>
     <string name="permdesc_route_media_output" msgid="4932818749547244346">"ឲ្យ​កម្មវិធី​នាំ​ផ្លូវ​លទ្ធផល​មេឌៀ​ទៅ​ឧបករណ៍​​ខាង​ក្រៅ​ផ្សេង។"</string>
-    <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូល​ដំណើរការ​ឧបករណ៍​ផ្ទុក​សុវត្ថិភាព​"</string>
+    <string name="permlab_access_keyguard_secure_storage" msgid="7565552237977815047">"ចូល​ដំណើរការ​ឧបករណ៍​ផ្ទុក​សុវត្ថិភាព"</string>
     <string name="permdesc_access_keyguard_secure_storage" msgid="5866245484303285762">"ឲ្យ​កម្មវិធី​ចូល​​ការ​ផ្ទុក​មាន​សុវត្ថិភាព keguard ។"</string>
     <string name="permlab_control_keyguard" msgid="172195184207828387">"ពិនិត្យ​ការ​បង្ហាញ និង​លាក់​ការ​ការពារ"</string>
     <string name="permdesc_control_keyguard" msgid="3043732290518629061">"ឲ្យ​កម្មវិធី​គ្រប់គ្រង keguard ។"</string>
@@ -1373,7 +1377,7 @@
     <string name="ime_action_go" msgid="8320845651737369027">"ទៅ"</string>
     <string name="ime_action_search" msgid="658110271822807811">"ស្វែងរក"</string>
     <string name="ime_action_send" msgid="2316166556349314424">"ផ្ញើ"</string>
-    <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់​"</string>
+    <string name="ime_action_next" msgid="3138843904009813834">"បន្ទាប់"</string>
     <string name="ime_action_done" msgid="8971516117910934605">"រួចរាល់"</string>
     <string name="ime_action_previous" msgid="1443550039250105948">"មុន"</string>
     <string name="ime_action_default" msgid="2840921885558045721">"អនុវត្ត"</string>
@@ -1382,7 +1386,7 @@
     <string name="grant_credentials_permission_message_header" msgid="2106103817937859662">"កម្មវិធី​មួយ ឬ​ច្រើន​ដូច​ខាង​ក្រោម​ស្នើ​សិទ្ធិ ដើម្បី​ចូល​គណនី​របស់​អ្នក​ឥឡូវ និង​ពេល​អនាគត។"</string>
     <string name="grant_credentials_permission_message_footer" msgid="3125211343379376561">"តើ​អ្នក​ចង់​អនុញ្ញាត​សំណើ​នេះ?"</string>
     <string name="grant_permissions_header_text" msgid="6874497408201826708">"ស្នើ​ចូល"</string>
-    <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត​"</string>
+    <string name="allow" msgid="7225948811296386551">"អនុញ្ញាត"</string>
     <string name="deny" msgid="2081879885755434506">"បដិសេធ"</string>
     <string name="permission_request_notification_title" msgid="6486759795926237907">"បាន​ស្នើ​សិទ្ធិ"</string>
     <string name="permission_request_notification_with_subtitle" msgid="8530393139639560189">"បាន​ស្នើ​សិទ្ធិ\nសម្រាប់​គណនី <xliff:g id="ACCOUNT">%s</xliff:g> ។"</string>
@@ -1405,12 +1409,12 @@
     <string name="no_file_chosen" msgid="6363648562170759465">"គ្មាន​ឯកសារ​បាន​ជ្រើស"</string>
     <string name="reset" msgid="2448168080964209908">"កំណត់​ឡើងវិញ"</string>
     <string name="submit" msgid="1602335572089911941">"ដាក់​ស្នើ"</string>
-    <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បាន​បើក​របៀប​រថយន្ត​"</string>
+    <string name="car_mode_disable_notification_title" msgid="3164768212003864316">"បាន​បើក​របៀប​រថយន្ត"</string>
     <string name="car_mode_disable_notification_message" msgid="8035230537563503262">"ប៉ះ​ ដើម្បី​ចេញ​ពី​របៀប​រថយន្ត​។"</string>
     <string name="tethered_notification_title" msgid="3146694234398202601">"ភ្ជាប់ ឬ​ហតស្ពត​សកម្ម"</string>
     <string name="tethered_notification_message" msgid="6857031760103062982">"ប៉ះ​ ដើម្បី​រៀបចំ។"</string>
     <string name="back_button_label" msgid="2300470004503343439">"ថយក្រោយ"</string>
-    <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់​"</string>
+    <string name="next_button_label" msgid="1080555104677992408">"បន្ទាប់"</string>
     <string name="skip_button_label" msgid="1275362299471631819">"រំលង"</string>
     <string name="throttle_warning_notification_title" msgid="4890894267454867276">"ការ​ប្រើ​ទិន្នន័យ​ចល័ត​ខ្ពស់"</string>
     <string name="throttle_warning_notification_message" msgid="3340822228599337743">"ប៉ះ​ ដើម្បី​​ស្វែងយល់​បន្ថែម​អំពី​ការ​ប្រើ​​​ទិន្នន័យ​ចល័ត​។"</string>
@@ -1436,7 +1440,7 @@
     <string name="media_shared" product="nosdcard" msgid="5830814349250834225">"ឧបករណ៍​ផ្ទុក​យូអេសប៊ី​បច្ចុប្បន្ន​កំពុង​ប្រើ​ដោយ​កុំព្យូទ័រ។"</string>
     <string name="media_shared" product="default" msgid="5706130568133540435">"បច្ចុប្បន្ន​កាត​អេសឌី​កំពុង​ប្រើ​ដោយ​កុំព្យូទ័រ"</string>
     <string name="media_unknown_state" msgid="729192782197290385">"មិន​ស្គាល់​ស្ថានភាព​មេឌៀ​ខាង​ក្រៅ។"</string>
-    <string name="share" msgid="1778686618230011964">"ចែក​រំលែក​"</string>
+    <string name="share" msgid="1778686618230011964">"ចែក​រំលែក"</string>
     <string name="find" msgid="4808270900322985960">"រក"</string>
     <string name="websearch" msgid="4337157977400211589">"ស្វែងរក​តាម​បណ្ដាញ"</string>
     <string name="find_next" msgid="5742124618942193978">"រក​បន្ទាប់"</string>
@@ -1452,7 +1456,7 @@
     <string name="sync_undo_deletes" msgid="2941317360600338602">"មិន​ធ្វើ​ការ​លុប​វិញ"</string>
     <string name="sync_do_nothing" msgid="3743764740430821845">"មិន​ធ្វើអ្វី​ទេ​ឥឡូវ"</string>
     <string name="choose_account_label" msgid="5655203089746423927">"ជ្រើស​គណនី"</string>
-    <string name="add_account_label" msgid="2935267344849993553">"បន្ថែម​គណនី​ថ្មី​​"</string>
+    <string name="add_account_label" msgid="2935267344849993553">"បន្ថែម​គណនី​ថ្មី"</string>
     <string name="add_account_button_label" msgid="3611982894853435874">"បន្ថែម​គណនី"</string>
     <string name="number_picker_increment_button" msgid="2412072272832284313">"បង្កើន"</string>
     <string name="number_picker_decrement_button" msgid="476050778386779067">"បន្ថយ"</string>
@@ -1471,15 +1475,15 @@
     <string name="date_picker_increment_year_button" msgid="6318697384310808899">"បង្កើន​​ឆ្នាំ"</string>
     <string name="date_picker_decrement_year_button" msgid="4482021813491121717">"បន្ថយ​ឆ្នាំ"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
-    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់​"</string>
+    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
     <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូរ​របៀប"</string>
     <string name="keyboardview_keycode_shift" msgid="2270748814315147690">"Shift"</string>
     <string name="keyboardview_keycode_enter" msgid="2985864015076059467">"Enter"</string>
-    <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើស​កម្មវិធី​​"</string>
+    <string name="activitychooserview_choose_application" msgid="2125168057199941199">"ជ្រើស​កម្មវិធី"</string>
     <string name="activitychooserview_choose_application_error" msgid="8624618365481126668">"មិន​អាច​ចាប់ផ្ដើម <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
-    <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែក​ជា​មួយ​"</string>
+    <string name="shareactionprovider_share_with" msgid="806688056141131819">"ចែករំលែក​ជា​មួយ"</string>
     <string name="shareactionprovider_share_with_application" msgid="5627411384638389738">"ចែក​រំលែក​ជា​មួយ <xliff:g id="APPLICATION_NAME">%s</xliff:g>"</string>
     <string name="content_description_sliding_handle" msgid="415975056159262248">"គ្រប់គ្រង​ការ​រុញ។ ប៉ះ &amp; សង្កត់។"</string>
     <string name="description_target_unlock_tablet" msgid="3833195335629795055">"អូស​ ដើម្បី​ដោះ​សោ។"</string>
@@ -1493,7 +1497,7 @@
     <string name="storage_internal" msgid="4891916833657929263">"ឧបករណ៍​ផ្ទុក​ខាង​ក្នុង"</string>
     <string name="storage_sd_card" msgid="3282948861378286745">"កាត​អេសឌី"</string>
     <string name="storage_usb" msgid="3017954059538517278">"ឧបករណ៍​ផ្ទុក​យូអេសប៊ី"</string>
-    <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល​"</string>
+    <string name="extract_edit_menu_button" msgid="8940478730496610137">"កែសម្រួល"</string>
     <string name="data_usage_warning_title" msgid="1955638862122232342">"ការព្រមាន​ប្រើ​ទិន្នន័យ"</string>
     <string name="data_usage_warning_body" msgid="2814673551471969954">"ប៉ះ ដើម្បី​មើល​ការ​ប្រើ និង​ការ​កំណត់។"</string>
     <string name="data_usage_3g_limit_title" msgid="7093334419518706686">"បាន​បិទ​ទិន្នន័យ 2G​-3G"</string>
@@ -1550,7 +1554,7 @@
     <string name="media_route_status_available" msgid="6983258067194649391">"ទំនេរ"</string>
     <string name="media_route_status_not_available" msgid="6739899962681886401">"មិន​ទំនេរ"</string>
     <string name="media_route_status_in_use" msgid="4533786031090198063">"កំពុង​ប្រើ"</string>
-    <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់​ជាប់​"</string>
+    <string name="display_manager_built_in_display_name" msgid="2583134294292563941">"អេក្រង់​ជាប់"</string>
     <string name="display_manager_hdmi_display_name" msgid="1555264559227470109">"អេក្រង់ HDMI"</string>
     <string name="display_manager_overlay_display_name" msgid="5142365982271620716">"#<xliff:g id="ID">%1$d</xliff:g> ត្រួត​គ្នា"</string>
     <string name="display_manager_overlay_display_title" msgid="652124517672257172">"<xliff:g id="NAME">%1$s</xliff:g>: <xliff:g id="WIDTH">%2$d</xliff:g>x<xliff:g id="HEIGHT">%3$d</xliff:g>, <xliff:g id="DPI">%4$d</xliff:g> dpi"</string>
@@ -1582,7 +1586,7 @@
     <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បី​ដោះ​សោ ចូល​ក្នុង​គណនី Google ។"</string>
     <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ី​ម៉ែ​ល​)"</string>
-    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់​"</string>
+    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
     <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
     <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេច​ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
@@ -1691,7 +1695,7 @@
     <string name="mediasize_japanese_you4" msgid="2091777168747058008">"You4"</string>
     <string name="mediasize_unknown_portrait" msgid="3088043641616409762">"​មិន​ស្គាល់​បញ្ឈរ"</string>
     <string name="mediasize_unknown_landscape" msgid="4876995327029361552">"មិន​ស្គាល់​ទេសភាព"</string>
-    <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បាន​បោះ​បង់​"</string>
+    <string name="write_fail_reason_cancelled" msgid="7091258378121627624">"បាន​បោះ​បង់"</string>
     <string name="write_fail_reason_cannot_write" msgid="8132505417935337724">"កំហុស​ក្នុង​ការ​សរសេរ​មាតិកា"</string>
     <string name="reason_unknown" msgid="6048913880184628119">"មិន​ស្គាល់"</string>
     <string name="reason_service_unavailable" msgid="7824008732243903268">"មិន​បា​ន​បើក​សេវាកម្ម​បោះពុម្ព"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 43d1b25..cffe6bb 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"앱이 설치된 알람 시계 앱에서 알람을 설정할 수 있도록 허용합니다. 일부 알람 시계 앱에는 이 기능이 구현되지 않을 수 있습니다."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"음성사서함 추가"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"앱이 음성사서함에 메시지를 추가할 수 있도록 허용합니다."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"브라우저 위치 정보 권한 수정"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"앱이 브라우저의 위치 정보 권한을 수정할 수 있도록 허용합니다. 이 경우 악성 앱이 이 기능을 이용하여 임의의 웹사이트에 위치 정보를 보낼 수 있습니다."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"패키지 확인"</string>
@@ -1362,10 +1366,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"애플리케이션이 Trust 상태에서의 변경사항을 수신할 수 있도록 허용합니다."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Trust Agent 제공"</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"애플리케이션이 Trust Agent를 제공할 수 있도록 허용합니다."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Trust Agent 설정 메뉴를 실행합니다."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"애플리케이션에서 Trust Agent의 동작을 변경하는 활동을 실행하도록 허용합니다."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Trust Agent 서비스에 연결"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"애플리케이션이 Trust Agent 서비스에 바인딩할 수 있도록 허용합니다."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"업데이트 및 복구 시스템과 상호작용"</string>
diff --git a/core/res/res/values-lo-rLA/strings.xml b/core/res/res/values-lo-rLA/strings.xml
index e386a24..b44c9e0 100644
--- a/core/res/res/values-lo-rLA/strings.xml
+++ b/core/res/res/values-lo-rLA/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"ອະນຸຍາດໃຫ້ແອັບຯຕັ້ງໂມງປຸກໃນແອັບຯໂມງປຸກທີ່ຕິດຕັ້ງໄວ້. ບາງແອັບຯໂມງປຸກອາດບໍ່ມີຄຸນສົມບັດແບບນີ້ເທື່ອ."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"ເພີ່ມຂໍ້ຄວາມສຽງ"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"ອະນຸຍາດໃຫ້ແອັບຯ ສາມາດເພີ່ມຂໍ້ຄວາມໃສ່ອິນບັອກຂໍ້ຄວາມສຽງຂອງທ່ານໄດ້."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"ອ່ານ​ຂໍ້​ຄວາມ​ສຽງ​ທັງ​ໝົດ"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"​ອະ​ນຸ​ຍາດ​ໃຫ້​ແອັບຯ​ອ່ານ​ຂໍ້​ຄວາມ​ສຽງ​ທັງ​ໝົດ​ຂອງ​ທ່ານ."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ແກ້ໄຂສິດທາງສະຖານທີ່ພູມສາດຂອງໂປຣແກຣມທ່ອງເວັບ"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"ອະນຸຍາດໃຫ້ແອັບຯແກ້ໄຂ ການອະນຸຍາດຕຳແໜ່ງທາງພູມສາດ ຂອງໂປຣແກຣມທ່ອງເວັບ. ແອັບຯທີ່ເປັນອັນຕະລາຍອາດໃຊ້ຄຸນສົມບັດນີ້ ເພື່ອສົ່ງຂໍ້ມູນສະຖານທີ່ໄປໃຫ້ເວັບໄຊຕ່າງໆໄດ້."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ຢັ້ງຢືນແພັກເກດ"</string>
@@ -1703,7 +1705,7 @@
     <string name="restr_pin_enter_old_pin" msgid="1462206225512910757">"PIN ປະ​ຈຸ​ບັນ"</string>
     <string name="restr_pin_enter_new_pin" msgid="5959606691619959184">"ລະຫັດ PIN ໃໝ່"</string>
     <string name="restr_pin_confirm_pin" msgid="8501523829633146239">"ຢືນຢັນລະຫັດ PIN ໃໝ່"</string>
-    <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາ​ລັບ​ການ​ປັບ​ປຸງ​ຂໍ້ຈໍາ​ກັດ​"</string>
+    <string name="restr_pin_create_pin" msgid="8017600000263450337">"ສ້າງ PIN ສໍາ​ລັບ​ການ​ປັບ​ປຸງ​ຂໍ້ຈໍາ​ກັດ"</string>
     <string name="restr_pin_error_doesnt_match" msgid="2224214190906994548">"PIN ບໍ່​ກົງກັນ. ລອງໃໝ່ອີກຄັ້ງ​."</string>
     <string name="restr_pin_error_too_short" msgid="8173982756265777792">"PIN ​ສັ້ນ​ເກີນ​ໄປ​. ຕ້ອງມີຢ່າງໜ້ອຍ 4 ຫຼັກ​."</string>
   <plurals name="restr_pin_countdown">
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index be7e571..82548a0 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Leidžiama programai nustatyti signalą įdiegtoje žadintuvo programoje. Kai kuriose žadintuvo programose ši funkcija gali nebūti nevykdoma."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"pridėti balso pašto pranešimų"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Leidžia programai pridėti pranešimų prie jūsų balso pašto gautųjų."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"skaityti visus balso pašto pranešimus"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Programai leidžiama skaityti visus balso pašto pranešimus."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"keisti naršyklės geografinės vietos leidimus"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Leidžiama programai keisti naršyklės geografinės vietos leidimus. Kenkėjiškos programos gali tai naudoti, kad leistų siųsti vietos informaciją abejotinoms svetainėms."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"patikrinti paketus"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index b421da4..798a39d 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Ļauj lietotnei iestatīt signālu instalētajā modinātājpulksteņa lietotnē. Dažās modinātājpulksteņu lietotnēs šo funkciju, iespējams, nevar ieviest."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"pievienot balss pastu"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ļauj lietotnei pievienot ziņojumus jūsu balss pasta iesūtnei."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"pārveidot pārlūkprogrammas ģeogrāfiskās atrašanās vietas atļaujas"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ļauj lietotnei modificēt pārlūkprogrammas ģeogrāfiskās atrašanās vietas atļaujas. Ļaunprātīgas lietotnes to var izmantot, lai atļautu atrašanās vietas informācijas sūtīšanu uz citām vietnēm."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"pakotņu verificēšana"</string>
diff --git a/core/res/res/values-mn-rMN/strings.xml b/core/res/res/values-mn-rMN/strings.xml
index 803128f..d19d607 100644
--- a/core/res/res/values-mn-rMN/strings.xml
+++ b/core/res/res/values-mn-rMN/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Апп нь суулгагдсан сэрүүлэгний апп дээр сэрүүлэг тохируулах боломжтой. Зарим сэрүүлэгний апп нь энэ функцийг дэмжихгүй байж болзошгүй."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"дуут шуудан нэмэх"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Таны дуут шуудангийн ирсэн мэйлд зурвас нэмэхийг апп-д зөвшөөрөх."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"бүх дуут шууданг унших"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Апп-д таны бүх дуут шууданг унших боломж олгоно."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Хөтчийн геобайршлын зөвшөөрлийг өөрчлөх"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Апп нь Хөтчийн гео байршлын зөвшөөрлийг өөрчлөх боломжтой. Хортой апп нь энийг ашиглан дурын веб хуудасруу байршлын мэдээллийг илгээх боломжтой."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"багцийг тулгах"</string>
diff --git a/core/res/res/values-ms-rMY/strings.xml b/core/res/res/values-ms-rMY/strings.xml
index 1d1a75b..49347a7 100644
--- a/core/res/res/values-ms-rMY/strings.xml
+++ b/core/res/res/values-ms-rMY/strings.xml
@@ -189,8 +189,7 @@
     <string name="safeMode" msgid="2788228061547930246">"Mod selamat"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistem Android"</string>
     <string name="user_owner_label" msgid="6465364741001216388">"Apl peribadi"</string>
-    <!-- no translation found for managed_profile_label (4287077106125758391) -->
-    <skip />
+    <string name="managed_profile_label" msgid="4287077106125758391">"Kerja Android"</string>
     <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Perkhidmatan yang anda perlu bayar"</string>
     <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Melakukan perkara yang boleh mengenakan bayaran kepada anda."</string>
     <string name="permgrouplab_messages" msgid="7521249148445456662">"Mesej anda"</string>
@@ -402,7 +401,7 @@
     <string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"terikat kepada interaksi suara"</string>
     <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi perkhidmatan interaksi suara. Tidak sekali-kali diperlukan untuk apl biasa."</string>
     <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"urus frasa kunci suara"</string>
-    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Membenarkan pemegang menguruskan frasa kunci untuk pengesahan sebutan laluan. Tidak sekali-kali diperlukan untuk apl biasa."</string>
+    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Membenarkan pemegang mengurus frasa kunci untuk pengesahan sebutan laluan suara. Tidak sekali-kali diperlukan untuk apl biasa."</string>
     <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"terikat kepada paparan jauh"</string>
     <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Membenarkan pemegang terikat dengan antara muka peringkat tertinggi bagi paparan jauh. Tidak sekali-kali diperlukan untuk apl biasa."</string>
     <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"terikat kepada perkhidmatan widget"</string>
@@ -1002,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Membenarkan apl untuk menetapkan penggera dalam apl penggera jam yang dipasang. Sesetengah applikasi jam penggera tidak boleh melaksanakan ciri ini."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"tambah mel suara"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Membenarkan apl untuk menambahkan mesej pada peti masuk mel suara anda."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"ubah suai kebenaran geolokasi Penyemak Imbas"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Membenarkan apl untuk mengubah suai kebenaran geolokasi Penyemak Imbas. Apl hasad boleh menggunakannya untuk membenarkan menghantar maklumat lokasi kepada laman web sembarangan."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"sahkan pakej"</string>
@@ -1363,10 +1366,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Membenarkan aplikasi mendengar perubahan dalam keadaan amanah."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Sediakan ejen amanah."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Membenarkan aplikasi menyediakan ejen amanah."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Lancarkan menu tetapan ejen amanah."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Membenarkan aplikasi melancarkan aktiviti yang mengubah tingkah laku ejen amanah."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Mengikat kepada perkhidmatan ejen amanah"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Membenarkan aplikasi terikat kepada perkhidmatan ejen amanah."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Berinteraksi dengan kemas kini dan sistem pemulihan"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 31fa11d..40764c7 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -401,7 +401,7 @@
     <string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"binde seg til en tjeneste for talehandlinger"</string>
     <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"Gir innehaveren tillatelse til å binde til toppnivået av brukergrensesnittet for en tjeneste for talehandlinger. Dette skal ikke være nødvendig for vanlige apper."</string>
     <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"administrer nøkkelfraser for stemmebruk"</string>
-    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Tillater innehaveren å administrere nøkkelfraser for gjenkjennelse av talekommandoord. Skal aldri være nødvendig for normale apper."</string>
+    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"Tillater eieren å administrere nøkkelfraser for gjenkjennelse av talekommandoord. Skal aldri være nødvendig for normale apper."</string>
     <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"binde til ekstern skjerm"</string>
     <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"Lar innehaveren binde seg til det øverste grensesnittnivået for ekstern skjerm. Skal aldri være nødvendig for vanlige apper."</string>
     <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"binde til modultjenste"</string>
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Lar appen stille inn alarmen for en installert alarmklokke-app. Enkelte alarmklokke-apper implementerer kanskje ikke denne funksjonen."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"legge til talepost"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Lar appen legge til meldinger i talepostkassen din."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"endre nettleserens tillatelser for geoposisjonering"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Lar appen endre nettleserens tillatelser for geoposisjonering. Ondsinnede apper kan bruke dette for å tillate sending av posisjonsinformasjon til vilkårlige nettsteder."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"bekrefte pakker"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index a655f36..37c2594 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Hiermee kan de app een alarm instellen in een geïnstalleerde wekkerapp. Deze functie wordt door sommige wekkerapps niet geïmplementeerd."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"voicemail toevoegen"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Hiermee kan de app berichten toevoegen aan de inbox van uw voicemail."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"alle voicemails lezen"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Hiermee kan de app al uw voicemails lezen."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"geolocatierechten voor browser aanpassen"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Hiermee kan de app de geolocatierechten van de browser aanpassen. Schadelijke apps kunnen dit gebruiken om locatiegegevens te verzenden naar willekeurige websites."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"pakketten controleren"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index c30ad74..a3282e9 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Pozwala aplikacji na ustawienie alarmu w zainstalowanej aplikacji budzika. Funkcja ta może nie być zaimplementowana w niektórych aplikacjach tego typu."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"dodawanie poczty głosowej"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Pozwala aplikacji na dodawanie wiadomości do skrzynki odbiorczej poczty głosowej."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"odczyt całej poczty głosowej"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Zezwala aplikacji na odczyt całej Twojej poczty głosowej."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modyfikowanie pozwoleń przeglądarki dotyczących lokalizacji geograficznej"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Pozwala aplikacji na modyfikowanie uprawnień przeglądarki dotyczących lokalizacji geograficznej. Złośliwe aplikacje mogą używać tej opcji do wysyłania informacji o lokalizacji do dowolnych witryn."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"weryfikowanie pakietów"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 2e4e423..e85d902 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que a aplicação defina um alarme numa aplicação de despertador instalada. Algumas aplicações de despertador podem não integrar esta funcionalidade."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"adicionar correio de voz"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que a aplicação adicione mensagens à sua caixa de entrada de correio de voz."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"ler todo o correio de voz"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Permite que a aplicação leia todo o correio de voz."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificar permissões de geolocalização do Navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que a aplicação modifique as permissões de geolocalização do navegador. As aplicações maliciosas podem usar isto para permitir o envio de informações de localização para Web sites arbitrárias."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificar pacotes"</string>
@@ -1362,10 +1364,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que uma aplicação registe alterações no trust state."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fornecer um agente fidedigno."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que uma aplicação forneça um agente fidedigno."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Inicie o menu de definições do agente de fidedignidade."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permite que uma aplicação inicie uma atividade que altere o comportamento do agente de fidedignidade."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Vincular a um serviço de trust agent"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite que uma aplicação fique vinculada a um serviço de trust agent."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Interagir com o sistema de recuperação e de atualização"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 3df7ecb..59328f5 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Permite que o aplicativo defina um alarme em um aplicativo despertador instalado. Alguns aplicativos despertador podem não implementar este recurso."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"adicionar correio de voz"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite que o aplicativo adicione mensagens a sua caixa de entrada do correio de voz."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Modifique as permissões de geolocalização de seu navegador"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite que o aplicativo modifique as permissões de geolocalização do navegador. Aplicativos maliciosos podem usar isso para permitir o envio de informações locais para sites arbitrários."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificar pacotes"</string>
@@ -1362,10 +1366,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite que o aplicativo detecte alterações no estado de confiança."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Fornecer um agente de confiança."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite que um aplicativo forneça um agente de confiança."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Abra o menu de configurações do agente de confiança."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permite que um aplicativo inicie uma atividade que altera o comportamento do agente de confiança."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Associar a um serviço de agente de confiança"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite que o aplicativo se associe a um serviço de agente de confiança."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Interagir com o sistema de atualizações e recuperação"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index ff26d8f..83ba3a6 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -1666,6 +1666,10 @@
     <skip />
     <!-- no translation found for permdesc_addVoicemail (6604508651428252437) -->
     <skip />
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <!-- no translation found for permlab_writeGeolocationPermissions (5962224158955273932) -->
     <skip />
     <!-- no translation found for permdesc_writeGeolocationPermissions (1083743234522638747) -->
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 110b243..11236b5 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -189,8 +189,7 @@
     <string name="safeMode" msgid="2788228061547930246">"Mod sigur"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Sistemul Android"</string>
     <string name="user_owner_label" msgid="6465364741001216388">"Aplicații personale"</string>
-    <!-- no translation found for managed_profile_label (4287077106125758391) -->
-    <skip />
+    <string name="managed_profile_label" msgid="4287077106125758391">"Android pentru serviciu"</string>
     <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Servicii cu plată"</string>
     <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Efectuează acţiuni care sunt cu plată."</string>
     <string name="permgrouplab_messages" msgid="7521249148445456662">"Mesajele dvs."</string>
@@ -1002,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Permite aplicaţiei să seteze o alarmă într-o aplicaţie de ceas cu alarmă instalată. Este posibil ca unele aplicaţii de ceas cu alarmă să nu implementeze această funcţie."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"adăugare mesagerie vocală"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Permite aplicaţiei să adauge mesaje în Mesaje primite în mesageria vocală."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"modificare permisiuni pentru locaţia geografică a browserului"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Permite aplicaţiei să modifice permisiunile privind locaţia geografică a browserului. Aplicaţiile rău intenţionate pot utiliza această permisiune pentru a permite trimiterea informaţiilor privind locaţia către site-uri web arbitrare."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"verificare pachete"</string>
@@ -1363,10 +1366,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Permite unei aplicații să detecteze modificările în starea de încredere."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Indicați un agent de încredere."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Permite unei aplicații să indice un agent de încredere."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Lansați meniul de setări pentru agentul de încredere."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Permite unei aplicații să lanseze o activitate care schimbă comportamentul agentului de încredere."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Asocierea la un serviciu „agenți de încredere”."</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Permite unei aplicații să se asocieze la un serviciu „agent de încredere”."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Interacțiune cu sistemul de recuperare și de actualizare"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index 12d1895..c5f4c13 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Приложение сможет настраивать будильник. Функция поддерживается не во всех программах."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"Добавление голосовых сообщений"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Приложение сможет добавлять голосовые сообщения в папку \"Входящие\"."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"Доступ к голосовой почте"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Чтение голосовых сообщений."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Изменение прав доступа к геоданным в браузере"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Приложение сможет изменять настройки доступа к геоданным в браузере. Вредоносные программы смогут таким образом отправлять информацию о местоположении на любые веб-сайты."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"Проверка пакетов"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index ab596c5..eb685ba 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Umožňuje aplikácii nastaviť budík v nainštalovanej aplikácii budík. Niektoré aplikácie budíka nemusia túto funkciu implementovať."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"pridať hlasovú schránku"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Umožní aplikácii pridávať správy do doručenej pošty hlasovej schránky."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"zmeniť povolenia prehliadača poskytovať informácie o zemepisnej polohe"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Umožňuje aplikácii zmeniť povolenia prehliadača na poskytovanie údajov o zemepisnej polohe. Škodlivé aplikácie to môžu použiť na odosielanie informácií o polohe ľubovoľným webovým stránkam."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"overiť balíky"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index cc06fd3..47568d6 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Programu omogoča nastavitev alarma v nameščenem programu budilke. Nekateri programi budilke morda nimajo te funkcije."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"dodajanje odzivnika"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Programu omogoča dodajanje sporočil prejetim sporočilom odzivnika."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Spreminjanje dovoljenj za geolokacijo brskalnika"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Programu omogoča spreminjanje geolokacijskih dovoljenj v brskalniku. Zlonamerni programi lahko to izkoristijo za pošiljanje podatkov o lokaciji poljubnim spletnim mestom."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"preveri pakete"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 099e6ef..4758c94 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -189,7 +189,7 @@
     <string name="safeMode" msgid="2788228061547930246">"Безбедни режим"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android систем"</string>
     <string name="user_owner_label" msgid="6465364741001216388">"Личне апликације"</string>
-    <string name="managed_profile_label" msgid="4287077106125758391">"Android за посао"</string>
+    <string name="managed_profile_label" msgid="4287077106125758391">"Android Work"</string>
     <string name="permgrouplab_costMoney" msgid="5429808217861460401">"Услуге које се плаћају"</string>
     <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"Покреће радње које могу да се плаћају."</string>
     <string name="permgrouplab_messages" msgid="7521249148445456662">"Поруке"</string>
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Дозвољава апликацији да подеси аларм у инсталираној апликацији будилника. Неке апликације будилника можда не примењују ову функцију."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"додавање говорне поште"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Дозвољава апликацији да додаје поруке у пријемно сандуче говорне поште."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"читај сву говорну пошту"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Дозвољава апликацији да чита говорну пошту."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"измена дозвола за географске локације Прегледача"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Дозвољава апликацији да измени дозволе Прегледача за утврђивање географске локације. Злонамерне апликације то могу да злоупотребе и искористе за слање информација о локацији насумичним веб сајтовима."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"верификовање пакета"</string>
@@ -1362,10 +1364,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Дозвољава апликацији да прати промене Trust стања."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Обезбеђивање поузданог агента."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Дозвољава апликацији да обезбеди поузданог агента."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Покрени мени подешавања поузданог агента."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Дозвољава апликацији да покреће активност која мења понашање поузданог агента."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Везивање за услугу Trust agents"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Дозвољава апликацији да се веже за услугу Trust agents."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Интеракција са системом за ажурирање и опоравак"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index 46bb563..af85340 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Tillåter att appen ställer in ett alarm i en befintlig alarmapp. Vissa alarmappar har inte den här funktionen."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"lägg till röstbrevlåda"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Gör att appen lägger till meddelanden i röstbrevlådans inkorg."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"läsa alla röstmeddelanden"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Tillåter att appen läser alla dina röstmeddelanden."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Ändra geografisk plats för webbläsaren"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillåter att appen ändrar webbläsarens behörigheter för geografisk plats. Skadliga appar kan använda detta för att tillåta att platsinformation skickas till godtyckliga webbplatser."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"kontrollera paket"</string>
@@ -1362,10 +1364,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Tillåter att en app lyssnar efter ändringar i den betrodda agentens status."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Tillhandahåll en betrodd agent."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Tillåter att en app tillhandahåller en betrodd agent."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Öppna inställningsmenyn för betrodda agenter."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Tillåter att en app startar en aktivitet som ändrar den betrodda agentens beteende."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Bind till en tjänst från en betrodd agent"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Tillåter att en app binds vid en tjänst från en betrodd agent."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Interagera med uppdaterings- och återställningssystemet"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 595c807..755b9d2 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Inaruhusu programu kuweka kengele katika programu iliyosakinishwa ya kengele. Programu zingine za kengele zinawezakosa kutekeleza kipengee hiki."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"ongeza barua ya sauti"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Huruhusu programu kuongeza mawasiliano kwenye kikasha cha ujumbe wa sauti."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"soma ujumbe wote wa sauti"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Huruhusu programu isome ujumbe wako wote wa sauti."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Rekebisha vibali vya Kivinjari cha eneo la jio"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Inaruhusu programu kurekebisha ruhusa za eneo la jio za kivinjari. Programu hasidi zinaweza tumia hii kuruhusu kutuma taarifa ya eneo kwa wavuti holela."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"thibitisha furushi"</string>
@@ -1362,10 +1364,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Huruhusu programu kusikiliza mabadiliko katika hali ya kuaminiwa."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Toa wakala wa uaminifu."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Huruhusu programu kutoa wakala wa uaminifu."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Fungua menyu ya mipangilio ya madalali wa kuaminiwa."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Huruhusu programu kufungua kitendo ambacho hubadilisha tabia ya madalali wa kuaminiwa."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Funga kwenye huduma ya dalali wa kuaminiwa"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Huruhusu programu kufungamanisha kwenye huduma ya dalali wa kuaminiwa."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Ingiliana na sasisho na mfumo wa kurejesha"</string>
diff --git a/core/res/res/values-television/themes.xml b/core/res/res/values-television/themes.xml
index f92ff40..3333f1b 100644
--- a/core/res/res/values-television/themes.xml
+++ b/core/res/res/values-television/themes.xml
@@ -16,8 +16,13 @@
 <resources>
     <style name="Theme.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
     <style name="Theme.Dialog.AppError" parent="Theme.Leanback.Dialog.AppError" />
+    <style name="Theme.Dialog.TimePicker" parent="Theme.Leanback.Dialog.TimePicker" />
     <style name="Theme.Holo.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
+    <style name="Theme.Holo.Dialog.TimePicker" parent="Theme.Leanback.Dialog.TimePicker" />
     <style name="Theme.Holo.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
+    <style name="Theme.Holo.Light.Dialog.TimePicker" parent="Theme.Leanback.Light.Dialog.TimePicker" />
     <style name="Theme.Material.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
+    <style name="Theme.Material.Dialog.TimePicker" parent="Theme.Leanback.Dialog.TimePicker" />
     <style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
+    <style name="Theme.Material.Light.Dialog.TimePicker" parent="Theme.Leanback.Light.Dialog.TimePicker" />
 </resources>
diff --git a/core/res/res/values-television/themes_device_defaults.xml b/core/res/res/values-television/themes_device_defaults.xml
index e01caa3..1938675 100644
--- a/core/res/res/values-television/themes_device_defaults.xml
+++ b/core/res/res/values-television/themes_device_defaults.xml
@@ -16,4 +16,6 @@
 <resources>
     <style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
     <style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
+    <style name="Theme.DeviceDefault.Dialog.TimePicker" parent="Theme.Leanback.Dialog.TimePicker" />
+    <style name="Theme.DeviceDefault.Light.Dialog.TimePicker" parent="Theme.Leanback.Light.Dialog.TimePicker" />
 </resources>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index feabf4f..b03221d 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -401,7 +401,7 @@
     <string name="permlab_bindVoiceInteraction" msgid="5334852580713715068">"เชื่อมโยงกับโปรแกรมโต้ตอบด้วยเสียง"</string>
     <string name="permdesc_bindVoiceInteraction" msgid="2345721766501778101">"อนุญาตให้ผู้ใช้อุปกรณ์เชื่อมโยงกับอินเทอร์เฟซระดับบนสุดของบริการโต้ตอบด้วยเสียง ไม่จำเป็นสำหรับแอปทั่วไป"</string>
     <string name="permlab_manageVoiceKeyphrases" msgid="1252285102392793548">"จัดการเสียงพูดวลีคำหลัก"</string>
-    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"ให้ผู้ใช้อุปกรณ์สามารถจัดการวลีคำหลักสำหรับการตรวจหาเสียงพูดคำที่นิยม ไม่จำเป็นสำหรับแอปทั่วไป"</string>
+    <string name="permdesc_manageVoiceKeyphrases" msgid="8476560722907530008">"ให้แอปสามารถจัดการวลีคำหลักสำหรับการตรวจหาเสียงพูดคำที่นิยม ไม่จำเป็นสำหรับแอปทั่วไป"</string>
     <string name="permlab_bindRemoteDisplay" msgid="1782923938029941960">"ผูกกับจอแสดงผลระยะไกล"</string>
     <string name="permdesc_bindRemoteDisplay" msgid="1261242718727295981">"อนุญาตให้ผู้ใช้ผูกกับอินเทอร์เฟซระดับสูงสุดของจอแสดงผลระยะไกล ซึ่งแอปพลิเคชันทั่วไปไม่จำเป็นต้องใช้"</string>
     <string name="permlab_bindRemoteViews" msgid="5697987759897367099">"เชื่อมโยงกับบริการวิดเจ็ต"</string>
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"อนุญาตให้แอปพลิเคชันตั้งเวลาปลุกในแอปพลิเคชันนาฬิกาปลุกที่ติดตั้ง แอปพลิเคชันนาฬิกาปลุกบางรายการอาจไม่ใช้คุณลักษณะนี้"</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"เพิ่มข้อวามเสียง"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"อนุญาตให้แอปพลิเคชันเพิ่มข้อความลงในกล่องข้อความเสียงของคุณ"</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"อ่านข้อความเสียงทั้งหมด"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"อนุญาตให้แอปอ่านข้อความเสียงทั้งหมดของคุณ"</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"แก้ไขการอนุญาตเกี่ยวกับการระบุตำแหน่งทางภูมิศาสตร์ของเบราว์เซอร์"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"อนุญาตให้แอปพลิเคชันแก้ไขการอนุญาตตำแหน่งทางภูมิศาสตร์ของเบราว์เซอร์ แอปพลิเคชันที่เป็นอันตรายอาจใช้การอนุญาตนี้ในการส่งข้อมูลตำแหน่งไปยังเว็บไซต์ต่างๆ ได้ตามต้องการ"</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"ยืนยันแพ็กเกจ"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 13b629e..af88c3c 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Pinapayagan ang app na magtakda ng alarm sa isang naka-install na app ng alarm clock. Maaaring hindi ipatupad ng ilang apps ng alarm clock ang tampok na ito."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"magdagdag ng voicemail"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Pinapayagan ang app na magdagdag ng mga mensahe sa iyong inbox ng voicemail."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"baguhin ang mga pahintulot ng geolocation ng Browser"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Pinapayagan ang app na baguhin ang mga pahintulot sa geolocation ng Browser. Maaari itong gamitin ng nakakahamak na apps upang payagan ang pagpapadala ng impormasyon ng lokasyon sa mga hindi tukoy na web site."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"i-verify ang mga package"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 0c19b03..ee8c464 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Uygulamaya, çalar saat uygulamasının alarmını ayarlama izni verir. Bazı çalar saat uygulamaları bu özelliği uygulayamayabilir."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"sesli mesaj ekle"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Uygulamaya, sesli mesaj gelen kutunuza mesaj ekleme izni verir."</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Tarayıcı\'nın coğrafi konum izinlerini değiştir"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Uygulamaya, Tarayıcı\'nın coğrafi konum izinlerini değiştirme izni verir. Kötü amaçlı uygulamalar keyfi web sitelerine konum bilgisi gönderilmesini sağlamak için bunu kullanabilirler."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"paketleri doğrula"</string>
@@ -1362,10 +1366,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Bir uygulamanın, güven durumundaki değişiklikleri dinlemesine izin verir."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Güven aracısı sağlama."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Bir uygulamanın güven aracısı sağlamasına izin verir."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Güven aracısı ayarlar menüsünü başlat."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Bir uygulamanın güven aracısının çalışma biçimini değiştiren bir etkinlik başlatmasına izin verir."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Güven aracı hizmetine bağlan"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Bir uygulamanın, güven aracı hizmetine bağlanmasına izin verir."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Güncelleme ve kurtarma sistemiyle etkileşim kur"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 0d56975b..b85fc99 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Дозволяє програмі налаштовувати сигнал у встановленій програмі будильника. У деяких програмах будильника ця функція може не застосовуватися."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"додавати голосову пошту"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Дозволяє програмі додавати повідомлення в папку \"Вхідні\" голосової пошти."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"читати всі голосові повідомлення"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Додаток може читати всі голосові повідомлення."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"змінювати дозволи географічного місцезнаходження у веб-переглядачі"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Дозволяє програмі змінювати дозволи географічного місцезнаходження у веб-переглядачі. Шкідливі програми можуть використовувати це, щоб дозволяти надсилати інформацію про місцезнаходження довільним веб-сайтам."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"перевіряти пакети"</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index dd13b45..b38fb87 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Cho phép ứng dụng đặt báo thức trong ứng dụng đồng hồ báo thức được cài đặt. Một số ứng dụng đồng hồ báo thức có thể không thực thi tính  năng này."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"thêm thư thoại"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Cho phép ứng dụng thêm thông báo vào hộp thư thoại đến của bạn."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"đọc tất cả các thư thoại"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Cho phép ứng dụng này đọc tất cả các thư thoại của bạn."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"sửa đổi các quyền về vị trí địa lý của Trình duyệt"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Cho phép ứng dụng sửa đổi cấp phép vị trí địa lý của Trình duyệt. Ứng dụng độc hại có thể lợi dụng quyền này để cho phép gửi thông tin vị trí tới các trang web tùy ý."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"xác minh gói"</string>
@@ -1362,10 +1364,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"Cho phép ứng dụng quan sát các thay đổi ở trạng thái đáng tin cậy."</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"Cung cấp tác nhân đáng tin cậy."</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"Cho phép ứng dụng cung cấp tác nhân đáng tin cậy."</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"Khởi chạy trình đơn cài đặt đại lý đáng tin cậy."</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"Cho phép ứng dụng khởi chạy hoạt động thay đổi hoạt động của đại lý đáng tin cậy."</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"Liên kết với một dịch vụ của đại lý đáng tin cậy"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"Cho phép ứng dụng liên kết với một dịch vụ của đại lý đáng tin cậy."</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"Tương tác với hệ thống khôi phục và bản cập nhật"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index 3a07366..d730c49 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -189,8 +189,7 @@
     <string name="safeMode" msgid="2788228061547930246">"安全模式"</string>
     <string name="android_system_label" msgid="6577375335728551336">"Android 系统"</string>
     <string name="user_owner_label" msgid="6465364741001216388">"个人应用"</string>
-    <!-- no translation found for managed_profile_label (4287077106125758391) -->
-    <skip />
+    <string name="managed_profile_label" msgid="4287077106125758391">"Android Work"</string>
     <string name="permgrouplab_costMoney" msgid="5429808217861460401">"需要您付费的服务"</string>
     <string name="permgroupdesc_costMoney" msgid="3293301903409869495">"执行可能需要您付费的操作。"</string>
     <string name="permgrouplab_messages" msgid="7521249148445456662">"您的信息"</string>
@@ -1002,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"允许应用在已安装的闹钟应用中设置闹钟。有些闹钟应用可能无法实现此功能。"</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"添加语音邮件"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"允许应用向您的语音信箱收件箱添加邮件。"</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"修改“浏览器”地理位置的权限"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允许应用修改“浏览器”的地理位置权限。恶意应用可能借此向任意网站发送位置信息。"</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"验证软件包"</string>
@@ -1363,10 +1366,8 @@
     <string name="permdesc_trust_listener" msgid="8233895334214716864">"允许应用检测信任状态的变化。"</string>
     <string name="permlab_provide_trust_agent" msgid="5465587586091358316">"提供信任的代理。"</string>
     <string name="permdesc_provide_trust_agent" msgid="3865702641053068148">"允许应用提供信任的代理。"</string>
-    <!-- no translation found for permlab_launch_trust_agent_settings (7494179366945389098) -->
-    <skip />
-    <!-- no translation found for permdesc_launch_trust_agent_settings (985453787420853278) -->
-    <skip />
+    <string name="permlab_launch_trust_agent_settings" msgid="7494179366945389098">"启动信任的代理设置菜单。"</string>
+    <string name="permdesc_launch_trust_agent_settings" msgid="985453787420853278">"允许应用启动可变更信任的代理行为的活动。"</string>
     <string name="permlab_bind_trust_agent_service" msgid="8242093169457695334">"绑定至信任的代理服务"</string>
     <string name="permdesc_bind_trust_agent_service" msgid="7041930026024507515">"允许应用绑定至信任的代理服务。"</string>
     <string name="permlab_recovery" msgid="3157024487744125846">"与更新和恢复系统互动"</string>
diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml
index 8e290b5..43a487b 100644
--- a/core/res/res/values-zh-rHK/strings.xml
+++ b/core/res/res/values-zh-rHK/strings.xml
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"允許應用程式在安裝的鬧鐘應用程式中設定鬧鐘,某些鬧鐘應用程式可能沒有這項功能。"</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"新增留言"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"允許應用程式將訊息加到您的留言信箱收件箱。"</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"修改瀏覽器地理資訊的權限"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允許應用程式修改瀏覽器的地理資訊權限。惡意應用程式可能會藉此允許將您的位置資訊任意傳送給某些網站。"</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"驗證套件"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 6ab2b48..6658689 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -448,7 +448,7 @@
     <string name="permlab_manageCaCertificates" msgid="1678391896786882014">"管理信任的憑證"</string>
     <string name="permdesc_manageCaCertificates" msgid="4015644047196937014">"允許應用程式安裝 CA 憑證 (做為信任的憑證) 及解除安裝 CA 憑證。"</string>
     <string name="permlab_bindJobService" msgid="3637568367978271086">"執行應用程式的預定背景作業"</string>
-    <string name="permdesc_bindJobService" msgid="3473288460524119838">"這項權限可讓 Android 系統於收到要求時在背景執行應用程式。"</string>
+    <string name="permdesc_bindJobService" msgid="3473288460524119838">"這項權限可讓 Android 系統收到要求時在背景執行應用程式。"</string>
     <string name="permlab_diagnostic" msgid="8076743953908000342">"讀寫 diag 擁有的資源"</string>
     <string name="permdesc_diagnostic" msgid="6608295692002452283">"允許應用程式讀取或寫入診斷群組擁有的任何資源,例如 /dev 底下的檔案。這可能會影響系統的穩定性和安全性,因此應由製造商或電信業者操作,且只用在特定硬體診斷。"</string>
     <string name="permlab_changeComponentState" msgid="6335576775711095931">"啟用或停用應用程式元件"</string>
@@ -1001,6 +1001,10 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"允許應用程式在安裝的鬧鐘應用程式中設定鬧鐘,某些鬧鐘應用程式可能無法執行這項功能。"</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"新增語音留言"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"允許應用程式將訊息新增至您的語音信箱收件匣。"</string>
+    <!-- no translation found for permlab_readAllVoicemail (5834057671176753416) -->
+    <skip />
+    <!-- no translation found for permdesc_readAllVoicemail (7429033637738774985) -->
+    <skip />
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"修改瀏覽器地理資訊的權限"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"允許應用程式修改瀏覽器的地理位置權限。請注意,惡意應用程式可能利用此功能允許將您的位置資訊任意傳送給某些網站。"</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"驗證套件"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index 07238c3..820ea50 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -1001,6 +1001,8 @@
     <string name="permdesc_setAlarm" msgid="316392039157473848">"Ivumela uhlelo lokusebenza ukuthi isethe i-alamu ensizeni efkiwe ye-alamu. Ezinye izinhlelo zokusebenza ze-alamu kungenzeka zingakusebenzisi lokho."</string>
     <string name="permlab_addVoicemail" msgid="5525660026090959044">"engeza imeyili yezwi"</string>
     <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Ivumela uhlelo lokusebenza ukwengeza imiyalezo kwibhokisi lakho lemeyili yezwi."</string>
+    <string name="permlab_readAllVoicemail" msgid="5834057671176753416">"funda wonke amavoyisimeyili"</string>
+    <string name="permdesc_readAllVoicemail" msgid="7429033637738774985">"Ivumela uhlelo lokusebenza ukuthi lufunde wonke amavoyisimeyili wakho."</string>
     <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"Gugula izimvume zendawo Yesiphequluli"</string>
     <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Ivumela uhlelo lokusebenza ukuthi iguqule izimvume eziphathelene nezindawo Zesiphequluli. Izuhlelo lokusebenza eziyingozi zingasebenzisa lokhu ukuvumela ukuvumela imininingwane yendawo kunoma imaphi amawebusayithi."</string>
     <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"qinisekisa amaphakheji"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index e807d69..a8a4d7a 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2424,7 +2424,7 @@
 
         <!-- Names a View such that it can be identified for Transitions. Names should be
              unique in the View hierarchy. -->
-        <attr name="viewName" format="string" />
+        <attr name="transitionName" format="string" />
 
         <!-- Specifies that this view should permit nested scrolling within a compatible
              ancestor view. -->
@@ -2432,6 +2432,30 @@
 
         <!-- Sets the state-based animator for the View. -->
         <attr name="stateListAnimator" format="reference"/>
+
+        <!-- Tint to apply to the background. -->
+        <attr name="backgroundTint" format="color" />
+
+        <!-- Blending mode used to apply the background tint. -->
+        <attr name="backgroundTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
     </declare-styleable>
 
     <!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -3021,7 +3045,29 @@
         <!-- Indicates the initial checked state of this button. -->
         <attr name="checked" format="boolean" />
         <!-- Drawable used for the button graphic (e.g. checkbox, radio button, etc). -->
-        <attr name="button" format="reference"/>
+        <attr name="button" format="reference" />
+        <!-- Tint to apply to the button graphic. -->
+        <attr name="buttonTint" format="color" />
+        <!-- Blending mode used to apply the button graphic tint. -->
+        <attr name="buttonTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
     </declare-styleable>
     <declare-styleable name="CheckedTextView">
         <!-- Indicates the initial checked state of this text. -->
@@ -3106,6 +3152,28 @@
         <!-- Determines whether to measure all children or just those in
              the VISIBLE or INVISIBLE state when measuring. Defaults to false. -->
         <attr name="measureAllChildren" format="boolean" />
+        <!-- Tint to apply to the foreground. -->
+        <attr name="foregroundTint" format="color" />
+        <!-- Blending mode used to apply the foreground tint. -->
+        <attr name="foregroundTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
     </declare-styleable>
     <declare-styleable name="ExpandableListView">
         <!-- Indicator shown beside the group View. This can be a stateful Drawable. -->
@@ -3211,6 +3279,10 @@
         <!-- @hide The alpha value (0-255) set on the ImageView's drawable. Equivalent
              to calling ImageView.setAlpha(int), not the same as View.setAlpha(float). -->
         <attr name="drawableAlpha" format="integer" />
+        <!-- Tint to apply to the image. -->
+        <attr name="tint" />
+        <!-- Blending mode used to apply the image tint. -->
+        <attr name="tintMode" />
     </declare-styleable>
     <declare-styleable name="ToggleButton">
         <!-- The text for the button when it is checked. -->
@@ -3401,6 +3473,98 @@
         <!-- Defines if the associated drawables need to be mirrored when in RTL mode.
              Default is false -->
         <attr name="mirrorForRtl" format="boolean" />
+        <!-- Tint to apply to the progress indicator. -->
+        <attr name="progressTint" format="color" />
+        <!-- Blending mode used to apply the progress indicator tint. -->
+        <attr name="progressTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
+        <!-- Tint to apply to the progress indicator background. -->
+        <attr name="progressBackgroundTint" format="color" />
+        <!-- Blending mode used to apply the progress indicator background tint. -->
+        <attr name="progressBackgroundTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
+        <!-- Tint to apply to the secondary progress indicator. -->
+        <attr name="secondaryProgressTint" format="color" />
+        <!-- Blending mode used to apply the secondary progress indicator tint. -->
+        <attr name="secondaryProgressTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
+        <!-- Tint to apply to the indepterminate progress indicator. -->
+        <attr name="indeterminateTint" format="color" />
+        <!-- Blending mode used to apply the indeterminate progress indicator tint. -->
+        <attr name="indeterminateTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
+        <!-- Tint to apply to the background. -->
+        <attr name="backgroundTint" />
+        <!-- Blending mode used to apply the background tint. -->
+        <attr name="backgroundTintMode" />
     </declare-styleable>
 
     <declare-styleable name="SeekBar">
@@ -3410,6 +3574,28 @@
         <attr name="thumbOffset" format="dimension" />
         <!-- Whether to split the track and leave a gap for the thumb drawable. -->
         <attr name="splitTrack" format="boolean" />
+        <!-- Tint to apply to the button graphic. -->
+        <attr name="thumbTint" format="color" />
+        <!-- Blending mode used to apply the button graphic tint. -->
+        <attr name="thumbTintMode">
+            <!-- The tint is drawn on top of the drawable.
+                 [Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc] -->
+            <enum name="src_over" value="3" />
+            <!-- The tint is masked by the alpha channel of the drawable. The drawable’s
+                 color channels are thrown out. [Sa * Da, Sc * Da] -->
+            <enum name="src_in" value="5" />
+            <!-- The tint is drawn above the drawable, but with the drawable’s alpha
+                 channel masking the result. [Da, Sc * Da + (1 - Sa) * Dc] -->
+            <enum name="src_atop" value="9" />
+            <!-- Multiplies the color and alpha channels of the drawable with those of
+                 the tint. [Sa * Da, Sc * Dc] -->
+            <enum name="multiply" value="14" />
+            <!-- [Sa + Da - Sa * Da, Sc + Dc - Sc * Dc] -->
+            <enum name="screen" value="15" />
+            <!-- Combines the tint and drawable color and alpha channels, clamping the
+                 result to valid color values. Saturate(S + D) -->
+            <enum name="add" value="16" />
+        </attr>
     </declare-styleable>
 
     <declare-styleable name="StackView">
@@ -4627,6 +4813,32 @@
                  mirror images so that adjacent images always seam. -->
             <enum name="mirror" value="2" />
         </attr>
+        <!-- Defines the horizontal tile mode. When the tile mode is enabled, the bitmap is repeated.
+             Gravity is ignored when the tile mode is enabled. Default value is "disabled". -->
+        <attr name="tileModeX">
+            <!-- Do not tile the bitmap. This is the default value. -->
+            <enum name="disabled" value="-1" />
+            <!-- Replicates the edge color. -->
+            <enum name="clamp" value="0" />
+            <!-- Repeats the bitmap in both direction. -->
+            <enum name="repeat" value="1" />
+            <!-- Repeats the shader's image horizontally and vertically, alternating
+                 mirror images so that adjacent images always seam. -->
+            <enum name="mirror" value="2" />
+        </attr>
+        <!-- Defines the vertical tile mode. When the tile mode is enabled, the bitmap is repeated.
+             Gravity is ignored when the tile mode is enabled. Default value is "disabled". -->
+        <attr name="tileModeY">
+            <!-- Do not tile the bitmap. This is the default value. -->
+            <enum name="disabled" value="-1" />
+            <!-- Replicates the edge color. -->
+            <enum name="clamp" value="0" />
+            <!-- Repeats the bitmap in both direction. -->
+            <enum name="repeat" value="1" />
+            <!-- Repeats the shader's image horizontally and vertically, alternating
+                 mirror images so that adjacent images always seam. -->
+            <enum name="mirror" value="2" />
+        </attr>
         <!-- Enables or disables the mipmap hint. See
             {@link android.graphics.Bitmap#setHasMipMap(boolean)} for more information.
             Default value is false. -->
@@ -4785,6 +4997,12 @@
         <attr name="height" />
         <!-- Enables or disables dithering. -->
         <attr name="dither" />
+        <!-- If set, specifies the color to apply to the drawable as a tint. By default,
+             no tint is applied. May be a color state list. -->
+        <attr name="tint" />
+        <!-- When a tint color is set, specifies its Porter-Duff blending mode. The
+             default value is src_in, which treats the drawable as an alpha mask. -->
+        <attr name="tintMode" />
     </declare-styleable>
 
     <!-- ========================== -->
@@ -4792,6 +5010,16 @@
     <!-- ========================== -->
     <eat-comment />
 
+    <!-- Drawable used to draw vector paths. -->
+    <declare-styleable name="VectorDrawable">
+        <!-- If set, specifies the color to apply to the drawable as a tint. By default,
+             no tint is applied. May be a color state list. -->
+        <attr name="tint" />
+        <!-- When a tint color is set, specifies its Porter-Duff blending mode. The
+             default value is src_in, which treats the drawable as an alpha mask. -->
+        <attr name="tintMode" />
+    </declare-styleable>
+
     <!-- Define the virtual size of the drawing surface paths will draw to. -->
     <declare-styleable name="VectorDrawableViewport">
         <!-- The width of the canvas the drawing is on. -->
@@ -5064,10 +5292,18 @@
     </declare-styleable>
 
     <declare-styleable name="PathInterpolator">
+        <!-- The x coordinate of the first control point of the cubic Bezier -->
         <attr name="controlX1" format="float" />
+        <!-- The y coordinate of the first control point of the cubic Bezier -->
         <attr name="controlY1" format="float" />
+        <!-- The x coordinate of the second control point of the cubic Bezier -->
         <attr name="controlX2" format="float" />
+        <!-- The y coordinate of the second control point of the cubic Bezier -->
         <attr name="controlY2" format="float" />
+        <!-- The control points defined as a path.
+             When pathData is defined, then both of the control points of the
+             cubic Bezier will be ignored. -->
+        <attr name="pathData"/>
     </declare-styleable>
 
     <!-- ========================== -->
@@ -5087,10 +5323,10 @@
         <attr name="interpolator" />
         <!-- The match order to use for the transition. This is a comma-separated
              list of values, containing one or more of the following:
-             id, itemId, viewName, instance. These correspond to
+             id, itemId, name, instance. These correspond to
              {@link android.transition.Transition#MATCH_ID},
              {@link android.transition.Transition#MATCH_ITEM_ID},
-             {@link android.transition.Transition#MATCH_VIEW_NAME}, and
+             {@link android.transition.Transition#MATCH_NAME}, and
              {@link android.transition.Transition#MATCH_INSTANCE}, respectively.
              This corresponds to {@link android.transition.Transition#setMatchOrder(int...)}. -->
         <attr name="matchOrder" format="string" />
@@ -5145,10 +5381,10 @@
         <attr name="targetClass" />
         <!-- The fully-qualified name of the Class to exclude from this transition. -->
         <attr name="excludeClass" format="string" />
-        <!-- The viewName of the target on which this transition will animation changes. -->
-        <attr name="targetViewName" format="string" />
-        <!-- The viewName of the target to exclude from this transition. -->
-        <attr name="excludeViewName" format="string" />
+        <!-- The transitionName of the target on which this transition will animation changes. -->
+        <attr name="targetName" format="string" />
+        <!-- The transitionName of the target to exclude from this transition. -->
+        <attr name="excludeName" format="string" />
     </declare-styleable>
 
     <!-- Use <code>set</code> as the root tag of the XML resource that
@@ -5206,6 +5442,9 @@
             <enum name="floatType" value="0" />
             <!-- valueFrom and valueTo are integers. -->
             <enum name="intType"   value="1" />
+            <!-- valueFrom and valueTo are paths defined as strings.
+                 This type is used for path morphing in AnimatedVectorDrawable. -->
+            <enum name="pathType"   value="2" />
         </attr>
     </declare-styleable>
 
@@ -5217,6 +5456,12 @@
     <declare-styleable name="PropertyAnimator">
         <!-- Name of the property being animated. -->
         <attr name="propertyName" format="string"/>
+        <!-- Name of the property being animated as the X coordinate of the pathData. -->
+        <attr name="propertyXName" format="string"/>
+        <!-- Name of the property being animated as the Y coordinate of the pathData. -->
+        <attr name="propertyYName" format="string"/>
+        <!-- The path used to animate the properties in the ObjectAnimator -->
+        <attr name="pathData"/>
     </declare-styleable>
 
 
@@ -6547,6 +6792,8 @@
         <attr name="switchPadding" format="dimension" />
         <!-- Whether to split the track and leave a gap for the thumb drawable. -->
         <attr name="splitTrack" />
+        <!-- Whether to draw on/off text. -->
+        <attr name="showText" format="boolean" />
     </declare-styleable>
 
     <declare-styleable name="Pointer">
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 814d8fc..fc1d0df 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -814,6 +814,14 @@
          via adb.  The default value of this attribute is <code>true</code>. -->
     <attr name="allowBackup" format="boolean" />
 
+    <!-- Indicates that even though the application provides a <code>BackupAgent</code>,
+         only full-data streaming backup operations are to be performed to save the app's
+         data.  This lets the app rely on full-data backups while still participating in
+         the backup and restore process via the BackupAgent's full-data backup APIs.
+         When this attribute is <code>true</code> the app's BackupAgent overrides of
+         the onBackup() and onRestore() callbacks can be empty stubs. -->
+    <attr name="fullBackupOnly" format="boolean" />
+
     <!-- Whether the application in question should be terminated after its
          settings have been restored during a full-system restore operation.
          Single-package restore operations will never cause the application to
@@ -873,17 +881,33 @@
     <!-- The name of the logical parent of the activity as it appears in the manifest. -->
     <attr name="parentActivityName" format="string" />
 
-    <!-- Define an activity that will persist across reboots. If such an activity is in the
-         Recents list when the device is shut off it will appear in the Recents list when
-         the device is next powered on. To be persisted all activities in the task from the
-         root activity up to the last activity before a <em>break</em> must be declared with
-         the persistable attribute. A <em>break</em> is the first activity after the root
-         started with Intent.FLAG_CLEAR_TASK_WHEN_RESET.
+    <!-- Define how an activity persist across reboots. Activities defined as "never" will not
+         be persisted. Those defined as "always" will be persisted. Those defined as "taskOnly"
+         will persist the root activity of the task only. See below for more detail as to
+         what gets persisted. -->
+    <attr name="persistableMode">
+        <!-- The default. If this activity forms the root of a task then that task will be
+             persisted across reboots but only the launching intent will be used. All
+             activities above this activity in the task will not be persisted. In addition
+             this activity will not be passed a PersistableBundle into which it could have
+             stored its state. -->
+        <enum name="persistRootOnly" value="0" />
+        <!-- If this activity forms the root of a task then that task will not be persisted
+             across reboots -->
+        <enum name="doNotPersist" value="1" />
+        <!-- If this activity forms the root of a task then the task and this activity will
+             be persisted across reboots. If the activity above this activity is also
+             tagged with the attribute <code>"persist"</code> then it will be persisted as well.
+             And so on up the task stack until either an activity without the
+             <code>persistableMode="persistAcrossReboots"</code> attribute or one that was launched
+             with the flag Intent.FLAG_CLEAR_TASK_WHEN_RESET is encountered.
 
-         <p>Activities that are declared with the persistable attribute will be provided with a
-         forced-persistable Bundle in onCreate() and onSavedInstanceState(), and must only
-         be passed a persistable Bundle in their Intent.extras. -->
-    <attr name="persistable" format="boolean" />
+             <p>Activities that are declared with the persistAcrossReboots attribute will be
+             provided with a PersistableBundle in onSavedInstanceState(), These activities may
+             use this PeristableBundle to save their state. Then, following a reboot, that
+             PersistableBundle will be provided back to the activity in its onCreate() method. -->
+        <enum name="persistAcrossReboots" value="2" />
+    </attr>
 
     <!-- This attribute specifies that an activity shall become the root activity of a
          new task each time it is launched. Using this attribute permits the user to
@@ -961,6 +985,14 @@
          Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} -->
     <attr name="autoRemoveFromRecents" format="boolean" />
 
+    <!-- Tasks whose root has this attribute set to true will replace baseIntent with that of the
+         next activity in the task. If the next activity also has this attribute set to true then
+         it will yield the baseIntent to any activity that it launches in the same task. This
+         continues until an activity is encountered which has this attribute set to false. False
+         is the default. This attribute set to true also permits activity's use of the
+         TaskDescription to change labels, colors and icons in the recent task list. -->
+    <attr name="relinquishTaskIdentity" format="boolean" />
+
     <!-- The <code>manifest</code> tag is the root of an
          <code>AndroidManifest.xml</code> file,
          describing the contents of an Android package (.apk) file.  One
@@ -1044,6 +1076,7 @@
         <attr name="testOnly" />
         <attr name="backupAgent" />
         <attr name="allowBackup" />
+        <attr name="fullBackupOnly" />
         <attr name="killAfterRestore" />
         <attr name="restoreNeedsApplication" />
         <attr name="restoreAnyVersion" />
@@ -1623,11 +1656,12 @@
         <!-- @hide This broacast receiver will only receive broadcasts for the
              primary user.  Can only be used with receivers. -->
         <attr name="primaryUserOnly" format="boolean" />
-        <attr name="persistable" />
+        <attr name="persistableMode" />
         <attr name="allowEmbedded" />
         <attr name="documentLaunchMode" />
         <attr name="maxRecents" />
         <attr name="autoRemoveFromRecents" />
+        <attr name="relinquishTaskIdentity" />
     </declare-styleable>
     
     <!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 9f6c7ad..dd316ed 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -125,9 +125,6 @@
 
     <color name="micro_text_light">#434343</color>
 
-    <color name="leanback_dark_gray">#ff333333</color>
-    <color name="leanback_light_gray">#ff888888</color>
-
     <drawable name="notification_template_icon_bg">#3333B5E5</drawable>
     <drawable name="notification_template_icon_low_bg">#0cffffff</drawable>
 
@@ -143,7 +140,7 @@
     <color name="keyguard_avatar_nick_color">#ffffffff</color>
     <color name="keyguard_avatar_frame_pressed_color">#ff35b5e5</color>
 
-    <color name="accessibility_focus_highlight">#80ffff00</color>
+    <color name="accessibility_focus_highlight">#bf39b500</color>
 
 </resources>
 
diff --git a/core/res/res/values/colors_leanback.xml b/core/res/res/values/colors_leanback.xml
new file mode 100644
index 0000000..dc0c673
--- /dev/null
+++ b/core/res/res/values/colors_leanback.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<!-- Colors specific to Leanback themes. -->
+<resources>
+    <color name="background_leanback_dark">#ff324248</color>
+    <color name="background_leanback_light">#ffeeeeee</color>
+
+    <color name="primary_text_leanback_dark">#cceeeeee</color>
+    <color name="secondary_text_leanback_dark">#99eeeeee</color>
+
+    <color name="primary_text_leanback_light">#cc222222</color>
+    <color name="secondary_text_leanback_light">#99222222</color>
+
+</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a4f78bd..d350ef2 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -1464,7 +1464,7 @@
 
     <!-- Name of the CustomDialog that is used for VPN -->
     <string name="config_customVpnConfirmDialogComponent"
-            >com.android.vpndialogs/com.android.vpndialogs.CustomDialog</string>
+            >com.android.vpndialogs/com.android.vpndialogs.ConfirmDialog</string>
 
     <!-- Apps that are authorized to access shared accounts, overridden by product overlays -->
     <string name="config_appsAuthorizedForSharedAccounts">;com.android.settings;</string>
@@ -1597,5 +1597,12 @@
         <item>"1,sim,0,modem"</item>
     </string-array>
 
+    <!-- This string array can be overriden to add an additional DRM support for WebView EME. -->
+    <!-- Array of "[keySystemName],[UuidOfMediaDrm]" -->
+    <string-array name="config_keySystemUuidMapping" translatable="false">
+        <!-- Example:
+        <item>"x-com.microsoft.playready,9A04F079-9840-4286-AB92-E65BE0885F95"</item>
+        -->
+    </string-array>
 
 </resources>
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index 639091e..c64e910 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -84,4 +84,5 @@
   <item type="id" name="current_scene" />
   <item type="id" name="scene_layoutid_cache" />
   <item type="id" name="mask" />
+  <item type="id" name="transitionPosition" />
 </resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 202c127..727d286 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2115,7 +2115,7 @@
   <public type="attr" name="controlY1" />
   <public type="attr" name="controlX2" />
   <public type="attr" name="controlY2" />
-  <public type="attr" name="viewName" />
+  <public type="attr" name="transitionName" />
   <public type="attr" name="transitionGroup" />
   <public type="attr" name="viewportWidth" />
   <public type="attr" name="viewportHeight" />
@@ -2136,7 +2136,7 @@
   <public type="attr" name="colorControlActivated" />
   <public type="attr" name="colorButtonNormal" />
   <public type="attr" name="colorControlHighlight" />
-  <public type="attr" name="persistable" />
+  <public type="attr" name="persistableMode" />
   <public type="attr" name="titleTextAppearance" />
   <public type="attr" name="subtitleTextAppearance" />
   <public type="attr" name="slideEdge" />
@@ -2168,8 +2168,8 @@
   <public type="attr" name="fromId" />
   <public type="attr" name="reversible" />
   <public type="attr" name="splitTrack" />
-  <public type="attr" name="targetViewName" />
-  <public type="attr" name="excludeViewName" />
+  <public type="attr" name="targetName" />
+  <public type="attr" name="excludeName" />
   <public type="attr" name="matchOrder" />
   <public type="attr" name="windowDrawsSystemBarBackgrounds" />
   <public type="attr" name="statusBarColor"/>
@@ -2190,6 +2190,31 @@
   <public type="attr" name="searchKeyphraseSupportedLocales" />
   <public type="attr" name="windowTransitionBackgroundFadeDuration" />
   <public type="attr" name="overlapAnchor" />
+  <public type="attr" name="progressTint" />
+  <public type="attr" name="progressTintMode" />
+  <public type="attr" name="progressBackgroundTint" />
+  <public type="attr" name="progressBackgroundTintMode" />
+  <public type="attr" name="secondaryProgressTint" />
+  <public type="attr" name="secondaryProgressTintMode" />
+  <public type="attr" name="indeterminateTint" />
+  <public type="attr" name="indeterminateTintMode" />
+  <public type="attr" name="backgroundTint" />
+  <public type="attr" name="backgroundTintMode" />
+  <public type="attr" name="foregroundTint" />
+  <public type="attr" name="foregroundTintMode" />
+  <public type="attr" name="buttonTint" />
+  <public type="attr" name="buttonTintMode" />
+  <public type="attr" name="thumbTint" />
+  <public type="attr" name="thumbTintMode" />
+  <public type="attr" name="fullBackupOnly" />
+  <public type="attr" name="propertyXName" />
+  <public type="attr" name="propertyYName" />
+  <public type="attr" name="relinquishTaskIdentity" />
+  <public type="attr" name="tileModeX" />
+  <public type="attr" name="tileModeY" />
+  <public type="attr" name="actionModeShareDrawable" />
+  <public type="attr" name="actionModeFindDrawable" />
+  <public type="attr" name="actionModeWebSearchDrawable" />
 
   <public-padding type="dimen" name="l_resource_pad" end="0x01050010" />
 
@@ -2441,5 +2466,26 @@
   <!-- An interpolator which accelerates fast and keeps accelerating until the end. -->
   <public type="interpolator" name="fast_out_linear_in" />
 
+  <!-- Used for Activity Transitions, this transition indicates that no Transition
+       should be used. -->
   <public type="transition" name="no_transition" id="0x010f0000"/>
+  <!-- A transition that moves and resizes a view -->
+  <public type="transition" name="move"/>
+  <!-- A transition that fades views in and out. -->
+  <public type="transition" name="fade"/>
+  <!-- A transition that moves views in or out of the scene to or from the edges when
+       a view visibility changes. -->
+  <public type="transition" name="explode"/>
+  <!-- A transition that moves views in or out of the scene to or from the bottom edge when
+       a view visibility changes. -->
+  <public type="transition" name="slide_bottom"/>
+  <!-- A transition that moves views in or out of the scene to or from the top edge when
+       a view visibility changes. -->
+  <public type="transition" name="slide_top"/>
+  <!-- A transition that moves views in or out of the scene to or from the right edge when
+       a view visibility changes. -->
+  <public type="transition" name="slide_right"/>
+  <!-- A transition that moves views in or out of the scene to or from the left edge when
+       a view visibility changes. -->
+  <public type="transition" name="slide_left"/>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6e1b5ef..e017f53 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2971,6 +2971,13 @@
       to your voicemail inbox.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether
+        they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+    <string name="permlab_readAllVoicemail">read all voicemail</string>
+    <!-- Description of an application permission, listed so the user can choose whether
+        they want to allow the application to do this. [CHAR LIMIT=NONE] -->
+    <string name="permdesc_readAllVoicemail">Allows the app to read all your voicemails.</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_writeGeolocationPermissions">modify Browser geolocation permissions</string>
     <!-- Description of an application permission, listed so the user can choose whether
@@ -4721,6 +4728,13 @@
     <!-- Accessibility announcement when a number that had been typed in is deleted [CHAR_LIMIT=NONE] -->
     <string name="deleted_key"><xliff:g id="key" example="4">%1$s</xliff:g> deleted</string>
 
+    <!--
+        Used to wrap a label for content description for a managed profile, e.g. "Work Email" instead
+        of email when there are two email apps.
+        [CHAR LIMIT=20]
+     -->
+    <string name="managed_profile_label_badge">Work <xliff:g id="label" example="Email">%1$s</xliff:g></string>
+
     <!-- DO NOT TRANSLATE -->
     <string name="time_placeholder">--</string>
 
@@ -4732,4 +4746,17 @@
     <!-- DO NOT TRANSLATE -->
     <string name="day_of_week_label_typeface">sans-serif</string>
 
+    <!-- Lock-to-app dialog title. -->
+    <string name="lock_to_app_title">Use lock-to-app?</string>
+    <!-- Lock-to-app dialog description. The $ is not actually shown or translated, it is a marker of where the recents icon shows up. -->
+    <string name="lock_to_app_description">Lock-to-app locks the display in a single app.\n\nTo exit press and hold the recent apps button  $</string>
+    <!-- Lock-to-app negative response. -->
+    <string name="lock_to_app_negative">NO</string>
+    <!-- Lock-to-app positive response. -->
+    <string name="lock_to_app_positive">START</string>
+    <!-- Starting lock-to-app indication. -->
+    <string name="lock_to_app_start">Start Lock-to-app</string>
+    <!-- Exting lock-to-app indication. -->
+    <string name="lock_to_app_exit">Exit Lock-to-app</string>
+
 </resources>
diff --git a/core/res/res/values/styles_leanback.xml b/core/res/res/values/styles_leanback.xml
index a37da4e..72d0379 100644
--- a/core/res/res/values/styles_leanback.xml
+++ b/core/res/res/values/styles_leanback.xml
@@ -14,46 +14,12 @@
      limitations under the License.
 -->
 <resources>
-    <style name="DialogWindowTitle.Leanback" parent="DialogWindowTitle.Holo">
-        <item name="android:textAppearance">@style/TextAppearance.Leanback.DialogWindowTitle</item>
-    </style>
-
-    <style name="TextAppearance.Leanback.DialogWindowTitle" parent="TextAppearance.Holo.DialogWindowTitle">
-        <item name="android:fontFamily">sans-serif-condensed</item>
-        <item name="android:textColor">?attr/textColorPrimary</item>
-    </style>
-
-    <style name="Leanback.ButtonBar" parent="Holo.ButtonBar">
-        <item name="showDividers">none</item>
-    </style>
-
-    <style name="AlertDialog.Leanback" parent="AlertDialog.Holo">
+    <style name="AlertDialog.Leanback" parent="AlertDialog.Material">
         <item name="layout">@android:layout/alert_dialog_leanback</item>
         <item name="buttonPanelSideLayout">@android:layout/alert_dialog_leanback_button_panel_right</item>
-        <item name="progressLayout">@android:layout/progress_dialog_leanback</item>
-        <item name="fullDark">@android:color/background_dark</item>
-        <item name="topDark">@android:color/background_dark</item>
-        <item name="centerDark">@android:color/background_dark</item>
-        <item name="bottomDark">@android:color/background_dark</item>
-        <item name="fullBright">@android:color/background_dark</item>
-        <item name="topBright">@android:color/background_dark</item>
-        <item name="centerBright">@android:color/background_dark</item>
-        <item name="bottomBright">@android:color/background_dark</item>
-        <item name="bottomMedium">@android:color/background_dark</item>
-        <item name="centerMedium">@android:color/background_dark</item>
     </style>
 
     <style name="AlertDialog.Leanback.Light">
-        <item name="fullDark">@android:color/leanback_light_gray</item>
-        <item name="topDark">@android:color/leanback_light_gray</item>
-        <item name="centerDark">@android:color/leanback_light_gray</item>
-        <item name="bottomDark">@android:color/leanback_light_gray</item>
-        <item name="fullBright">@android:color/leanback_light_gray</item>
-        <item name="topBright">@android:color/leanback_light_gray</item>
-        <item name="centerBright">@android:color/leanback_light_gray</item>
-        <item name="bottomBright">@android:color/leanback_light_gray</item>
-        <item name="bottomMedium">@android:color/leanback_light_gray</item>
-        <item name="centerMedium">@android:color/leanback_light_gray</item>
     </style>
 
 </resources>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 25307b9..75f905c 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -493,11 +493,12 @@
         <item name="thumb">@drawable/switch_thumb_material_anim</item>
         <item name="splitTrack">true</item>
         <item name="switchTextAppearance">@style/TextAppearance.Material.Widget.Switch</item>
-        <item name="textOn"></item>
-        <item name="textOff"></item>
+        <item name="textOn">@string/capital_on</item>
+        <item name="textOff">@string/capital_off</item>
         <item name="switchMinWidth">4dip</item>
         <item name="switchPadding">4dip</item>
         <item name="background">?attr/selectableItemBackgroundBorderless</item>
+        <item name="showText">false</item>
     </style>
 
     <style name="Widget.Material.EditText" parent="Widget.EditText"/>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5f4553b..3e82d08 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -218,6 +218,7 @@
   <java-symbol type="id" name="pin_error_message" />
   <java-symbol type="id" name="timePickerLayout" />
   <java-symbol type="id" name="profile_icon" />
+  <java-symbol type="id" name="transitionPosition" />
 
   <java-symbol type="attr" name="actionModeShareDrawable" />
   <java-symbol type="attr" name="alertDialogCenterButtons" />
@@ -603,6 +604,14 @@
   <java-symbol type="string" name="kilobyteShort" />
   <java-symbol type="string" name="last_month" />
   <java-symbol type="string" name="launchBrowserDefault" />
+  <java-symbol type="string" name="lock_to_app_title" />
+  <java-symbol type="string" name="lock_to_app_description" />
+  <java-symbol type="string" name="lock_to_app_negative" />
+  <java-symbol type="string" name="lock_to_app_positive" />
+  <java-symbol type="drawable" name="ic_recent" />
+  <java-symbol type="layout" name="lock_to_app_enter" />
+  <java-symbol type="layout" name="lock_to_app_exit" />
+  <java-symbol type="drawable" name="lock_task_notify_bg" />
   <java-symbol type="string" name="lockscreen_access_pattern_cell_added" />
   <java-symbol type="string" name="lockscreen_access_pattern_cleared" />
   <java-symbol type="string" name="lockscreen_access_pattern_detected" />
@@ -863,6 +872,7 @@
   <java-symbol type="string" name="action_bar_home_description_format" />
   <java-symbol type="string" name="action_bar_home_subtitle_description_format" />
   <java-symbol type="string" name="wireless_display_route_description" />
+  <java-symbol type="string" name="managed_profile_label_badge" />
   <java-symbol type="string" name="mediasize_unknown_portrait" />
   <java-symbol type="string" name="mediasize_unknown_landscape" />
   <java-symbol type="string" name="mediasize_iso_a0" />
@@ -1000,6 +1010,7 @@
   <java-symbol type="array" name="config_callBarringMMI" />
   <java-symbol type="array" name="config_globalActionsList" />
   <java-symbol type="array" name="config_telephonyHardware" />
+  <java-symbol type="array" name="config_keySystemUuidMapping" />
 
   <java-symbol type="drawable" name="default_wallpaper" />
   <java-symbol type="drawable" name="indicator_input_error" />
diff --git a/core/res/res/values/themes_leanback.xml b/core/res/res/values/themes_leanback.xml
index eba8dec..a571b98 100644
--- a/core/res/res/values/themes_leanback.xml
+++ b/core/res/res/values/themes_leanback.xml
@@ -14,26 +14,38 @@
      limitations under the License.
 -->
 <resources>
-    <style name="Theme.Leanback.Dialog.Alert" parent="Theme.Holo.Dialog.BaseAlert">
-      <item name="windowTitleStyle">@style/DialogWindowTitle.Leanback</item>
+    <style name="Theme.Leanback.Dialog.Alert" parent="Theme.Material.Dialog.BaseAlert">
+      <item name="colorBackground">@color/background_leanback_dark</item>
+      <item name="textColorPrimary">@color/primary_text_leanback_dark</item>
+      <item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
       <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
-      <item name="buttonBarStyle">@style/Leanback.ButtonBar</item>
-      <item name="listDividerAlertDialog">@null</item>
     </style>
 
-    <style name="Theme.Leanback.Light.Dialog.Alert" parent="Theme.Holo.Light.Dialog.BaseAlert">
-      <item name="windowTitleStyle">@style/DialogWindowTitle.Leanback</item>
+    <style name="Theme.Leanback.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.BaseAlert">
+      <item name="colorBackground">@color/background_leanback_light</item>
+      <item name="textColorPrimary">@color/primary_text_leanback_light</item>
+      <item name="textColorSecondary">@color/secondary_text_leanback_light</item>
       <item name="alertDialogStyle">@style/AlertDialog.Leanback.Light</item>
-      <item name="buttonBarStyle">@style/Leanback.ButtonBar</item>
-      <item name="listDividerAlertDialog">@null</item>
     </style>
 
-    <style name="Theme.Leanback.Light.Dialog" parent="Theme.Holo.Light.Dialog">
-        <item name="windowTitleStyle">@style/DialogWindowTitle.Leanback</item>
+    <style name="Theme.Leanback.Dialog.TimePicker" parent="Theme.Material.Dialog.BaseTimePicker">
+      <item name="colorBackground">@color/background_leanback_dark</item>
+      <item name="textColorPrimary">@color/primary_text_leanback_dark</item>
+      <item name="textColorSecondary">@color/secondary_text_leanback_dark</item>
+      <item name="alertDialogStyle">@style/AlertDialog.Leanback</item>
     </style>
 
-    <style name="Theme.Leanback.Dialog" parent="Theme.Holo.Dialog">
-        <item name="windowTitleStyle">@style/DialogWindowTitle.Leanback</item>
+    <style name="Theme.Leanback.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.BaseTimePicker">
+      <item name="colorBackground">@color/background_leanback_light</item>
+      <item name="textColorPrimary">@color/primary_text_leanback_light</item>
+      <item name="textColorSecondary">@color/secondary_text_leanback_light</item>
+      <item name="alertDialogStyle">@style/AlertDialog.Leanback.Light</item>
+    </style>
+
+    <style name="Theme.Leanback.Light.Dialog" parent="Theme.Material.Light.Dialog">
+    </style>
+
+    <style name="Theme.Leanback.Dialog" parent="Theme.Material.Dialog">
     </style>
 
     <style name="Theme.Leanback.Dialog.AppError" parent="Theme.Leanback.Dialog">
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 77e4307..b52b3a0 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1092,26 +1092,30 @@
         <item name="windowCloseOnTouchOutside">false</item>
     </style>
 
-    <!-- Material theme for alert dialog windows, which is used by the
-         {@link android.app.AlertDialog} class.  This is basically a dialog
-         but sets the background to empty so it can do two-tone backgrounds.
-         For applications targeting Honeycomb or newer, this is the default
-         AlertDialog theme. -->
-    <style name="Theme.Material.Dialog.Alert">
+    <style name="Theme.Material.Dialog.BaseAlert">
         <item name="windowBackground">@color/transparent</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
         <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
         <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
     </style>
 
-    <!-- Material theme for the TimePicker dialog windows, which is used by the
-         {@link android.app.TimePickerDialog} class. -->
-    <style name="Theme.Material.Dialog.TimePicker">
+    <!-- Material theme for alert dialog windows, which is used by the
+         {@link android.app.AlertDialog} class.  This is basically a dialog
+         but sets the background to empty so it can do two-tone backgrounds.
+         For applications targeting Honeycomb or newer, this is the default
+         AlertDialog theme. -->
+    <style name="Theme.Material.Dialog.Alert" parent="Theme.Material.Dialog.BaseAlert"/>
+
+    <style name="Theme.Material.Dialog.BaseTimePicker">
         <item name="windowBackground">@color/transparent</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material</item>
         <item name="windowContentOverlay">@null</item>
     </style>
 
+    <!-- Material theme for the TimePicker dialog windows, which is used by the
+         {@link android.app.TimePickerDialog} class. -->
+    <style name="Theme.Material.Dialog.TimePicker" parent="Theme.Material.Dialog.BaseTimePicker"/>
+
     <!-- Theme for a window that will be displayed either full-screen on
          smaller screens (small, normal) or as a dialog on larger screens
          (large, xlarge). -->
@@ -1206,25 +1210,29 @@
          (large, xlarge). -->
     <style name="Theme.Material.Light.DialogWhenLarge.NoActionBar" parent="@style/Theme.Material.Light.NoActionBar" />
 
-    <!-- Material light theme for alert dialog windows, which is used by the
-         {@link android.app.AlertDialog} class.  This is basically a dialog
-         but sets the background to empty so it can do two-tone backgrounds.
-         For applications targeting Honeycomb or newer, this is the default
-         AlertDialog theme. -->
-    <style name="Theme.Material.Light.Dialog.Alert">
+    <style name="Theme.Material.Light.Dialog.BaseAlert">
         <item name="windowBackground">@color/transparent</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
         <item name="windowMinWidthMajor">@dimen/dialog_min_width_major</item>
         <item name="windowMinWidthMinor">@dimen/dialog_min_width_minor</item>
     </style>
 
-    <!-- Material Light theme for the TimePicker dialog windows, which is used by the
-         {@link android.app.TimePickerDialog} class. -->
-    <style name="Theme.Material.Light.Dialog.TimePicker">
+    <!-- Material light theme for alert dialog windows, which is used by the
+         {@link android.app.AlertDialog} class.  This is basically a dialog
+         but sets the background to empty so it can do two-tone backgrounds.
+         For applications targeting Honeycomb or newer, this is the default
+         AlertDialog theme. -->
+    <style name="Theme.Material.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.BaseAlert"/>
+
+    <style name="Theme.Material.Light.Dialog.BaseTimePicker">
         <item name="windowBackground">@color/transparent</item>
         <item name="windowTitleStyle">@style/DialogWindowTitle.Material.Light</item>
     </style>
 
+    <!-- Material Light theme for the TimePicker dialog windows, which is used by the
+         {@link android.app.TimePickerDialog} class. -->
+    <style name="Theme.Material.Light.Dialog.TimePicker" parent="Theme.Material.Light.Dialog.BaseTimePicker"/>
+
     <!-- Theme for a presentation window on a secondary display. -->
     <style name="Theme.Material.Light.Dialog.Presentation" parent="@style/Theme.Material.Light.NoActionBar.Fullscreen" />
 
diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
index 7251e7c..7f41ac1c 100644
--- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
+++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java
@@ -305,11 +305,9 @@
 
     private PackageParser.Package parsePackage(Uri packageURI) throws PackageParserException {
         final String archiveFilePath = packageURI.getPath();
-        PackageParser packageParser = new PackageParser(archiveFilePath);
+        PackageParser packageParser = new PackageParser();
         File sourceFile = new File(archiveFilePath);
-        DisplayMetrics metrics = new DisplayMetrics();
-        metrics.setToDefaults();
-        PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, metrics, 0);
+        PackageParser.Package pkg = packageParser.parseMonolithicPackage(sourceFile, 0);
         packageParser = null;
         return pkg;
     }
diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf
index 2b75113..8e70e80 100644
--- a/data/fonts/DroidSansFallback.ttf
+++ b/data/fonts/DroidSansFallback.ttf
Binary files differ
diff --git a/data/fonts/DroidSansFallbackFull.ttf b/data/fonts/DroidSansFallbackFull.ttf
index a9df005..68d2751 100644
--- a/data/fonts/DroidSansFallbackFull.ttf
+++ b/data/fonts/DroidSansFallbackFull.ttf
Binary files differ
diff --git a/data/fonts/DroidSansFallbackLegacy.ttf b/data/fonts/DroidSansFallbackLegacy.ttf
deleted file mode 100644
index 61460b1..0000000
--- a/data/fonts/DroidSansFallbackLegacy.ttf
+++ /dev/null
Binary files differ
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 1eaae65..37e9b15 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -219,6 +219,21 @@
     </family>
     <family>
         <fileset>
+            <file>NotoSansCherokee-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>NotoSansCanadianAboriginal-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
+            <file>NotoSansYi-Regular.ttf</file>
+        </fileset>
+    </family>
+    <family>
+        <fileset>
             <file lang="zh-CN">NotoSansHans-Regular.otf</file>
         </fileset>
     </family>
diff --git a/docs/html/auto/images/assets/00_frame.png b/docs/html/auto/images/assets/00_frame.png
new file mode 100644
index 0000000..2a78380
--- /dev/null
+++ b/docs/html/auto/images/assets/00_frame.png
Binary files differ
diff --git a/docs/html/auto/images/assets/01_a_maps.png b/docs/html/auto/images/assets/01_a_maps.png
new file mode 100644
index 0000000..0d1b219
--- /dev/null
+++ b/docs/html/auto/images/assets/01_a_maps.png
Binary files differ
diff --git a/docs/html/auto/images/assets/01_b_now.png b/docs/html/auto/images/assets/01_b_now.png
new file mode 100644
index 0000000..043ec74
--- /dev/null
+++ b/docs/html/auto/images/assets/01_b_now.png
Binary files differ
diff --git a/docs/html/auto/images/assets/02_a_notif.gif b/docs/html/auto/images/assets/02_a_notif.gif
new file mode 100644
index 0000000..9f3d35d
--- /dev/null
+++ b/docs/html/auto/images/assets/02_a_notif.gif
Binary files differ
diff --git a/docs/html/auto/images/assets/02_b_switcher.gif b/docs/html/auto/images/assets/02_b_switcher.gif
new file mode 100644
index 0000000..c4014e3
--- /dev/null
+++ b/docs/html/auto/images/assets/02_b_switcher.gif
Binary files differ
diff --git a/docs/html/auto/images/assets/03_a_musict.png b/docs/html/auto/images/assets/03_a_musict.png
new file mode 100644
index 0000000..4e11243
--- /dev/null
+++ b/docs/html/auto/images/assets/03_a_musict.png
Binary files differ
diff --git a/docs/html/auto/images/assets/03_b_voice.gif b/docs/html/auto/images/assets/03_b_voice.gif
new file mode 100644
index 0000000..b280a9b
--- /dev/null
+++ b/docs/html/auto/images/assets/03_b_voice.gif
Binary files differ
diff --git a/docs/html/auto/images/assets/do_01_switcher.png b/docs/html/auto/images/assets/do_01_switcher.png
new file mode 100644
index 0000000..b654a13
--- /dev/null
+++ b/docs/html/auto/images/assets/do_01_switcher.png
Binary files differ
diff --git a/docs/html/auto/images/assets/do_02_music.png b/docs/html/auto/images/assets/do_02_music.png
new file mode 100644
index 0000000..59fc8df
--- /dev/null
+++ b/docs/html/auto/images/assets/do_02_music.png
Binary files differ
diff --git a/docs/html/auto/images/assets/do_02_music_night.png b/docs/html/auto/images/assets/do_02_music_night.png
new file mode 100644
index 0000000..7faccfb
--- /dev/null
+++ b/docs/html/auto/images/assets/do_02_music_night.png
Binary files differ
diff --git a/docs/html/auto/images/assets/do_03_more.png b/docs/html/auto/images/assets/do_03_more.png
new file mode 100644
index 0000000..17040d1
--- /dev/null
+++ b/docs/html/auto/images/assets/do_03_more.png
Binary files differ
diff --git a/docs/html/auto/images/assets/do_04_mdrawer.png b/docs/html/auto/images/assets/do_04_mdrawer.png
new file mode 100644
index 0000000..de94c22
--- /dev/null
+++ b/docs/html/auto/images/assets/do_04_mdrawer.png
Binary files differ
diff --git a/docs/html/auto/images/assets/do_05_template.png b/docs/html/auto/images/assets/do_05_template.png
new file mode 100644
index 0000000..63a07d8
--- /dev/null
+++ b/docs/html/auto/images/assets/do_05_template.png
Binary files differ
diff --git a/docs/html/auto/images/assets/do_05_template_night.png b/docs/html/auto/images/assets/do_05_template_night.png
new file mode 100644
index 0000000..6acd259
--- /dev/null
+++ b/docs/html/auto/images/assets/do_05_template_night.png
Binary files differ
diff --git a/docs/html/auto/images/assets/do_06_gdrawer.png b/docs/html/auto/images/assets/do_06_gdrawer.png
new file mode 100644
index 0000000..b02c0e6
--- /dev/null
+++ b/docs/html/auto/images/assets/do_06_gdrawer.png
Binary files differ
diff --git a/docs/html/auto/images/assets/do_07_notif.png b/docs/html/auto/images/assets/do_07_notif.png
new file mode 100644
index 0000000..50ea565
--- /dev/null
+++ b/docs/html/auto/images/assets/do_07_notif.png
Binary files differ
diff --git a/docs/html/auto/images/carlogos.png b/docs/html/auto/images/carlogos.png
deleted file mode 100644
index 3522aa3..0000000
--- a/docs/html/auto/images/carlogos.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/auto/images/figure02.png b/docs/html/auto/images/figure02.png
deleted file mode 100644
index 1b87224..0000000
--- a/docs/html/auto/images/figure02.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/auto/images/figure03.png b/docs/html/auto/images/figure03.png
deleted file mode 100644
index 0c4e20d..0000000
--- a/docs/html/auto/images/figure03.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/auto/images/figure04.png b/docs/html/auto/images/figure04.png
deleted file mode 100644
index 3e9c894..0000000
--- a/docs/html/auto/images/figure04.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/auto/images/figure05.png b/docs/html/auto/images/figure05.png
deleted file mode 100644
index 9c1d9b8..0000000
--- a/docs/html/auto/images/figure05.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/auto/images/figure06.png b/docs/html/auto/images/figure06.png
deleted file mode 100644
index 8140f0c..0000000
--- a/docs/html/auto/images/figure06.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/auto/images/figure07.png b/docs/html/auto/images/figure07.png
deleted file mode 100644
index 82c1c76..0000000
--- a/docs/html/auto/images/figure07.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/auto/images/figure08.png b/docs/html/auto/images/figure08.png
deleted file mode 100644
index f11e3a8..0000000
--- a/docs/html/auto/images/figure08.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/auto/images/hero.jpg b/docs/html/auto/images/hero.jpg
index 3f9114f..34b5b5d 100644
--- a/docs/html/auto/images/hero.jpg
+++ b/docs/html/auto/images/hero.jpg
Binary files differ
diff --git a/docs/html/auto/images/logos/apps/iheartradio.png b/docs/html/auto/images/logos/apps/iheartradio.png
new file mode 100644
index 0000000..8feb38d
--- /dev/null
+++ b/docs/html/auto/images/logos/apps/iheartradio.png
Binary files differ
diff --git a/docs/html/auto/images/logos/apps/joyride.png b/docs/html/auto/images/logos/apps/joyride.png
new file mode 100644
index 0000000..8c2897e
--- /dev/null
+++ b/docs/html/auto/images/logos/apps/joyride.png
Binary files differ
diff --git a/docs/html/auto/images/logos/apps/mlb.png b/docs/html/auto/images/logos/apps/mlb.png
new file mode 100644
index 0000000..ddde4cd
--- /dev/null
+++ b/docs/html/auto/images/logos/apps/mlb.png
Binary files differ
diff --git a/docs/html/auto/images/logos/apps/pandora.png b/docs/html/auto/images/logos/apps/pandora.png
new file mode 100644
index 0000000..13fa1d3
--- /dev/null
+++ b/docs/html/auto/images/logos/apps/pandora.png
Binary files differ
diff --git a/docs/html/auto/images/logos/apps/pocketcasts.png b/docs/html/auto/images/logos/apps/pocketcasts.png
new file mode 100644
index 0000000..5227937
--- /dev/null
+++ b/docs/html/auto/images/logos/apps/pocketcasts.png
Binary files differ
diff --git a/docs/html/auto/images/logos/apps/songza.png b/docs/html/auto/images/logos/apps/songza.png
new file mode 100644
index 0000000..3bd7107
--- /dev/null
+++ b/docs/html/auto/images/logos/apps/songza.png
Binary files differ
diff --git a/docs/html/auto/images/logos/apps/spotify.png b/docs/html/auto/images/logos/apps/spotify.png
new file mode 100644
index 0000000..d039fd3
--- /dev/null
+++ b/docs/html/auto/images/logos/apps/spotify.png
Binary files differ
diff --git a/docs/html/auto/images/logos/apps/stitcher.png b/docs/html/auto/images/logos/apps/stitcher.png
new file mode 100644
index 0000000..b77da12
--- /dev/null
+++ b/docs/html/auto/images/logos/apps/stitcher.png
Binary files differ
diff --git a/docs/html/auto/images/logos/apps/tunein.png b/docs/html/auto/images/logos/apps/tunein.png
new file mode 100644
index 0000000..137ba81
--- /dev/null
+++ b/docs/html/auto/images/logos/apps/tunein.png
Binary files differ
diff --git a/docs/html/auto/images/logos/apps/umano.png b/docs/html/auto/images/logos/apps/umano.png
new file mode 100644
index 0000000..7b59f95
--- /dev/null
+++ b/docs/html/auto/images/logos/apps/umano.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/abarth.png b/docs/html/auto/images/logos/auto/abarth.png
new file mode 100644
index 0000000..fd1d228
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/abarth.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/acura.png b/docs/html/auto/images/logos/auto/acura.png
new file mode 100644
index 0000000..4ba69cc
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/acura.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/alpharomeo.png b/docs/html/auto/images/logos/auto/alpharomeo.png
new file mode 100644
index 0000000..51c8c58
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/alpharomeo.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/audi.png b/docs/html/auto/images/logos/auto/audi.png
new file mode 100644
index 0000000..86cb06b
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/audi.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/bentley.png b/docs/html/auto/images/logos/auto/bentley.png
new file mode 100644
index 0000000..ed8cf47
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/bentley.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/chevrolet.png b/docs/html/auto/images/logos/auto/chevrolet.png
new file mode 100644
index 0000000..e0980b0
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/chevrolet.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/chrysler.png b/docs/html/auto/images/logos/auto/chrysler.png
new file mode 100644
index 0000000..c670c19
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/chrysler.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/datsun.png b/docs/html/auto/images/logos/auto/datsun.png
new file mode 100644
index 0000000..059ffd8
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/datsun.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/dodge.png b/docs/html/auto/images/logos/auto/dodge.png
new file mode 100644
index 0000000..4d6fcc0
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/dodge.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/fiat.png b/docs/html/auto/images/logos/auto/fiat.png
new file mode 100644
index 0000000..b9783d9
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/fiat.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/ford.png b/docs/html/auto/images/logos/auto/ford.png
new file mode 100644
index 0000000..86a2670
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/ford.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/honda.png b/docs/html/auto/images/logos/auto/honda.png
new file mode 100644
index 0000000..62ee38f
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/honda.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/hyundai.png b/docs/html/auto/images/logos/auto/hyundai.png
new file mode 100644
index 0000000..4744641
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/hyundai.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/infinity.png b/docs/html/auto/images/logos/auto/infinity.png
new file mode 100644
index 0000000..a20e0a0
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/infinity.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/jeep.png b/docs/html/auto/images/logos/auto/jeep.png
new file mode 100644
index 0000000..024c3d1
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/jeep.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/kia.png b/docs/html/auto/images/logos/auto/kia.png
new file mode 100644
index 0000000..f8046d9
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/kia.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/maserati.png b/docs/html/auto/images/logos/auto/maserati.png
new file mode 100644
index 0000000..22bcf6a
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/maserati.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/mazda.png b/docs/html/auto/images/logos/auto/mazda.png
new file mode 100644
index 0000000..2281c90
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/mazda.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/mercedesbenz.png b/docs/html/auto/images/logos/auto/mercedesbenz.png
new file mode 100644
index 0000000..05fab0f
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/mercedesbenz.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/mitsubishi.png b/docs/html/auto/images/logos/auto/mitsubishi.png
new file mode 100644
index 0000000..1405b19
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/mitsubishi.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/nissan.png b/docs/html/auto/images/logos/auto/nissan.png
new file mode 100644
index 0000000..63bc4f7
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/nissan.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/opel.png b/docs/html/auto/images/logos/auto/opel.png
new file mode 100644
index 0000000..7e25ed5
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/opel.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/ram.png b/docs/html/auto/images/logos/auto/ram.png
new file mode 100644
index 0000000..9702587
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/ram.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/renault.png b/docs/html/auto/images/logos/auto/renault.png
new file mode 100644
index 0000000..65a4ae3
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/renault.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/seat.png b/docs/html/auto/images/logos/auto/seat.png
new file mode 100644
index 0000000..9802ccf
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/seat.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/skoda.png b/docs/html/auto/images/logos/auto/skoda.png
new file mode 100755
index 0000000..7bc06b0
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/skoda.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/subaru.png b/docs/html/auto/images/logos/auto/subaru.png
new file mode 100644
index 0000000..7407e61
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/subaru.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/suzuki.png b/docs/html/auto/images/logos/auto/suzuki.png
new file mode 100644
index 0000000..85fd7e5
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/suzuki.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/volkswagen.png b/docs/html/auto/images/logos/auto/volkswagen.png
new file mode 100644
index 0000000..febadf4
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/volkswagen.png
Binary files differ
diff --git a/docs/html/auto/images/logos/auto/volvo.png b/docs/html/auto/images/logos/auto/volvo.png
new file mode 100644
index 0000000..683af26
--- /dev/null
+++ b/docs/html/auto/images/logos/auto/volvo.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/alpine.png b/docs/html/auto/images/logos/hard/alpine.png
new file mode 100644
index 0000000..55f0e8a
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/alpine.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/clarion.png b/docs/html/auto/images/logos/hard/clarion.png
new file mode 100644
index 0000000..2288b99
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/clarion.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/cloudcar.png b/docs/html/auto/images/logos/hard/cloudcar.png
new file mode 100644
index 0000000..9e7e8b7
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/cloudcar.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/delphi.png b/docs/html/auto/images/logos/hard/delphi.png
new file mode 100644
index 0000000..a2a03de
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/delphi.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/freescale.png b/docs/html/auto/images/logos/hard/freescale.png
new file mode 100644
index 0000000..2909944
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/freescale.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/fujitsuten.png b/docs/html/auto/images/logos/hard/fujitsuten.png
new file mode 100644
index 0000000..70269e3
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/fujitsuten.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/harman.png b/docs/html/auto/images/logos/hard/harman.png
new file mode 100644
index 0000000..0fb295a
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/harman.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/jvc.png b/docs/html/auto/images/logos/hard/jvc.png
new file mode 100644
index 0000000..6a9c7ad
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/jvc.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/kenwood.png b/docs/html/auto/images/logos/hard/kenwood.png
new file mode 100644
index 0000000..3c80098
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/kenwood.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/lg.png b/docs/html/auto/images/logos/hard/lg.png
new file mode 100644
index 0000000..52feeff
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/lg.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/panasonic.png b/docs/html/auto/images/logos/hard/panasonic.png
new file mode 100644
index 0000000..b6aaaf1
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/panasonic.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/parrot.png b/docs/html/auto/images/logos/hard/parrot.png
new file mode 100644
index 0000000..d5bf41f
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/parrot.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/pioneer.png b/docs/html/auto/images/logos/hard/pioneer.png
new file mode 100644
index 0000000..307d71a
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/pioneer.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/renesas.png b/docs/html/auto/images/logos/hard/renesas.png
new file mode 100644
index 0000000..69ecab7
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/renesas.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/symphonyteleca.png b/docs/html/auto/images/logos/hard/symphonyteleca.png
new file mode 100644
index 0000000..0b3ebe5
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/symphonyteleca.png
Binary files differ
diff --git a/docs/html/auto/images/logos/hard/texasinstruments.png b/docs/html/auto/images/logos/hard/texasinstruments.png
new file mode 100644
index 0000000..87d80cb
--- /dev/null
+++ b/docs/html/auto/images/logos/hard/texasinstruments.png
Binary files differ
diff --git a/docs/html/auto/index.jd b/docs/html/auto/index.jd
index c7a3a4e..5ce6289 100644
--- a/docs/html/auto/index.jd
+++ b/docs/html/auto/index.jd
@@ -11,18 +11,54 @@
 #jd-content>.content-footer.wrap {
   display:none;
 }
-</style>
-
-<style>
-#footer {
-  display: none;
-}
-.content-footer {
-  display: none;
-}
 #hero-height {
   height:calc(100% - 100px);
 }
+.img-logo {
+  margin:0 auto;
+  display:block;
+  margin-bottom:-28px !important;
+}
+.img-logo-thin {
+  margin:0 auto;
+  display:block;
+  margin-bottom:-55px !important;
+}
+.col-5 {
+  width:170px;
+}
+.auto-img-container-cols {
+  position:relative;
+  margin-bottom:25px;
+  margin-top:25px;
+}
+.auto-img-frame-cols {
+  z-index:2;
+  position:relative;
+  width:460px;
+}
+.auto-img-shot-cols {
+  width:447px;
+  position:absolute;
+  top:8px;
+  left:7px;
+  z-index:1;
+}
+.sdk-link {
+  font-size:16px;
+  padding-left:5px;
+  padding-right:15px;
+}
+.sdk-link-title {
+  font-size:18px;
+  padding-left:5px;
+  padding-right:15px;
+}
+.sdk-link-desc {
+  font-size:14px;
+  padding-left:5px;
+  padding-right:15px;
+}
 </style>
 
 
@@ -40,13 +76,14 @@
                 <div class="landing-h1 hero">Android Auto</div>
                 <div class="landing-subhead hero">Entertainment and services on your dashboard</div>
                 <div class="landing-hero-description">
-                  <p>Display and control your handheld app in vehicles.
-                  Build apps with easy-to-use UI templates that
-                  let users keep their eyes on the road.</p>
+                  <p style="width:450px">Display and control your Android app in vehicles.
+                   Integrate your content with easy-to-use APIs and let Android Auto take
+                   care of the rest.</p>
                 </div>
 
               <div class="landing-body">
-                <a href="{@docRoot}auto/overview.html" class="landing-button landing-primary" style="margin-top: 40px;">
+                <a href="{@docRoot}auto/overview.html" class="landing-button landing-primary"
+                   style="margin-top:40px;">
                   Developer Overview
                 </a>
               </div>
@@ -67,21 +104,33 @@
 
       <div class="landing-section landing-gray-background" id="android-in-car">
         <div class="wrap">
-          <div class="landing-section-header">
-            <div class="landing-h1">Bringing Android to Your Car</div>
+          <div class="landing-section-h1">
+            <div class="landing-h1">Extending Android to Cars</div>
           </div>
-
           <div class="landing-body">
-            <p>When users connect their Android phone to an Android Auto enabled vehicle, the
-              system shows an interface that lets users select compatible apps and services to run.
-            </p>
-            <p>Android Auto provides new APIs and tools that your existing Android apps can
-              leverage to run on any compatible vehicle. Users interact with your apps through the
-              touch screen and the physical buttons on the vehicle’s dashboard. Your apps can also
-              respond to voice commands.
+            <div class="landing-subhead">Android Auto brings the Android experience to
+            cars with apps like Google Now and Maps.</div>
+            <div class="cols">
+              <div class="col-8">
+                <div class="auto-img-container-cols">
+                  <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+                  <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/01_b_now.png" />
+                </div>
+              </div>
+              <div class="col-8">
+                <div class="auto-img-container-cols">
+                  <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+                  <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/01_a_maps.png" />
+                </div>
+              </div>
+            </div>
+
+            <p>When users connect their Android phones to compatible vehicles, Android Auto
+             shows a standard interface that lets them start enabled apps and services.
+             Android Auto locks the handheld device when connected, so drivers
+            interact with Auto by using the vehicle's input controls, touch display, and voice.</p>
             </p>
           </div>
-
         </div> <!-- end .wrap -->
       </div> <!-- end .landing-section -->
 
@@ -89,112 +138,330 @@
       <div class="landing-section">
         <div class="wrap">
           <div class="landing-section-header">
-            <div class="landing-h1">One Platform</div>
+            <div class="landing-h1">Build for One Platform</div>
+            <div class="landing-subhead">Create apps with the Android APIs you're familiar with
+            and extend them to cars with the Auto SDK.
+            </div>
           </div>
 
           <div class="landing-body">
-            <p>Android Auto is an extension of the Android platform. You can easily adapt
-            existing apps for Android Auto and reuse many of the Android APIs and services you
-            are already familiar with.</p>
-            <p>The Android Auto platform and SDK let you write your apps only once, without having
+
+            <div class="cols">
+              <div class="col-8">
+                <div class="auto-img-container-cols">
+                  <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+                  <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/02_b_switcher.gif" />
+                </div>
+              </div>
+              <div class="col-8">
+                <div class="auto-img-container-cols">
+                  <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+                  <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/02_a_notif.gif" />
+                </div>
+              </div>
+            </div>
+            <p>Write your apps without having
             to worry about vehicle-specific hardware differences like screen resolution, software
-            interfaces, knobs and touch controls. Your app runs the same on any compatible
-            vehicle.</p>
+            interfaces, knobs, and touch controls. Your users get the same experience on any compatible
+            vehicle such as a consistent app launcher and system events such as notifications.</p>
           </div>
         </div> <!-- end .wrap -->
       </div> <!-- end .landing-section -->
 
+
+      <div class="landing-section landing-gray-background">
+        <div class="wrap">
+          <div class="landing-section-header">
+            <div class="landing-h1">Minimize Distraction</div>
+            <div class="landing-subhead">
+             Android Auto displays different UIs for several app categories that let users focus on the road.
+            </div>
+          </div>
+          <div class="landing-body">
+            <div class="cols">
+              <div class="col-8">
+                <div class="auto-img-container-cols">
+                  <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+                  <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/03_b_voice.gif" />
+                </div>
+              </div>
+              <div class="col-8">
+                <div class="auto-img-container-cols">
+                  <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+                  <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/03_a_musict.png" />
+                </div>
+              </div>
+            </div>
+            <p>Android Auto defines the user interaction model for all
+             apps and let you hook into a standard UI with touch and voice controls. The interface
+             is designed to reduce driver distraction while still letting you customize and brand them to properly deliver your content.
+             </p>
+          </div>
+        </div> <!-- end .wrap -->
+      </div> <!-- end .landing-section -->
+
+      <div class="landing-section" style="background-color:#f5f5f5">
+        <div class="wrap">
+          <div class="landing-section-header">
+            <div class="landing-pre-h1">Coming soon</div>
+            <div class="landing-h1">Android Auto SDK</div>
+          </div>
+
+          <div class="landing-body">
+            <p>In the coming months, we’ll be releasing the Android Auto SDK, which includes APIs
+              and tools to make your existing apps compatible with Android Auto. The first version
+              of the SDK will provide APIs for music, podcast, live radio, and audio
+              news apps, as well as limited voice actions.</p>
+
+            <div class="cols" style="margin-top:40px">
+              <div class="col-3-wide">
+                <p class="sdk-link-title">Updates</p>
+                <p class="sdk-link-desc">
+                  Register to receive more information and be notified when the SDK is available.
+                </p>
+                <a class="sdk-link" href="https://docs.google.com/a/google.com/forms/d/1ANgYOoYLkfyZ2JRPSU34Nep5yNaU-Ha2syXJ9b4xLrA/viewform">Sign up for updates</a>
+              </div>
+              <div class="col-3-wide">
+                <p class="sdk-link-title">Google+ Community</p>
+                <p class="sdk-link-desc">
+                  Stay involved, get updates, and exchange experiences with other developers.
+                </p>
+                <a class="sdk-link" href="http://g.co/androidautodev">Discuss on Google+</a>
+              </div>
+              <div class="col-3-wide">
+                <p class="sdk-link-title">Developer Overview</p>
+                <p class="sdk-link-desc">
+                  Learn more about developing apps for Android Auto when the SDK is available.
+                </p>
+                <a class="sdk-link" href="{@docRoot}auto/overview.html">Learn about the platform</a>
+              </div>
+            </div>
+          </div>
+
+        </div> <!-- end .wrap -->
+      </div> <!-- end .landing-section -->
+
+
+
       <div class="landing-section landing-gray-background" >
         <div class="wrap">
           <div class="landing-section-header">
-            <div class="landing-h1">Minimizing Distraction</div>
+            <div class="landing-h1">See What Others Have Done</div>
+            <div class="landing-subhead">We're working with developers to bring many popular apps
+            to Android Auto</div>
           </div>
-
           <div class="landing-body">
-            <p>We designed Android Auto to minimize driver distraction. Android Auto provides UI
-              templates for several app categories. These templates define the user interaction model
-              for any app and follow international best practices for reducing driver distraction.</p>
-            <p>You can customize these templates to fit your brand and link them to your app’s
-              content and functionality, instead of building new UIs and testing them for driver
-              distraction, which is a lengthy and costly process.</p>
-            <p>Android Auto locks any device that users connect to a compatible vehicle, so drivers
-              interact with the device using voice actions and the vehicle’s input controls.</p>
+          <div class="cols">
+
+            <div class="col-4">
+              <img src="{@docRoot}auto/images/logos/apps/joyride.png"
+                   width="160" height="160" class="img-logo" />
+            </div>
+            <div class="col-4">
+              <img src="{@docRoot}auto/images/logos/apps/mlb.png"
+                   width="160" height="160" class="img-logo" />
+            </div>
+            <div class="col-4">
+              <img src="{@docRoot}auto/images/logos/apps/pandora.png"
+                   width="160" height="160" class="img-logo" />
+            </div>
+            <div class="col-4">
+              <img src="{@docRoot}auto/images/logos/apps/pocketcasts.png"
+                   width="160" height="160" class="img-logo" />
+            </div>
+          </div>
+          <div class="cols">
+            <div class="col-4">
+              <img src="{@docRoot}auto/images/logos/apps/songza.png"
+                   width="160" height="160" class="img-logo" />
+            </div>
+            <div class="col-4">
+              <img src="{@docRoot}auto/images/logos/apps/stitcher.png"
+                   width="160" height="160" class="img-logo" />
+            </div>
+            <div class="col-4">
+              <img src="{@docRoot}auto/images/logos/apps/tunein.png"
+                   width="160" height="160" class="img-logo" />
+            </div>
+            <div class="col-4">
+              <img src="{@docRoot}auto/images/logos/apps/umano.png"
+                   width="160" height="160" class="img-logo" />
+            </div>
+          </div>
+          <div class="cols" style="margin-top:40px">
+              <div class="col-4">
+                <img src="{@docRoot}auto/images/logos/apps/iheartradio.png"
+                     width="160" height="160" class="img-logo" />
+              </div>
+              <div class="col-12">
+                <p><em>
+                "The Android Auto APIs provide an easy way to integrate the most important features and functionality of iHeartRadio’s robust music service into a safety-minded automotive infotainment solution. The process was seamless, utilizing a flexible construct that allowed us to quickly adapt our existing product without losing any of the core experience our listeners know and love."</em></p>
+              </div>
+          </div>
+          <div class="cols" style="margin-top:60px">
+
+              <div class="col-4">
+                <img src="{@docRoot}auto/images/logos/apps/spotify.png"
+                     width="160" height="160" class="img-logo" />
+              </div>
+              <div class="col-12"><p style="margin-top:20px"><em>
+              "Android Auto offers Spotify the exciting opportunity to easily enable safe access to millions of songs while driving. We were able to quickly develop for the platform using the new Android voice and media API extensions. As a result, Android users will soon be able to continue the Spotify experience in their cars, including being able to play any song, artist, album or playlist by voice.</em></p>
+              </div>
+              </div>
           </div>
         </div> <!-- end .wrap -->
       </div> <!-- end .landing-section -->
 
+
+
+
       <div class="landing-section landing-white-background">
         <div class="wrap">
           <div class="landing-section-header">
             <div class="landing-h1">Building an Ecosystem</div>
           </div>
           <div class="landing-body landing-align-center">
-              <p style="margin-bottom:20px">
-                Android Auto is coming soon to new cars from these manufacturers:
-              </p>
+              <div class="landing-subhead">
+                Android Auto is coming soon to new cars from these manufacturers
+              </div>
           </div>
-          <div style="width:800px;margin:0 auto">
-            <img src="{@docRoot}auto/images/carlogos.png" alt="Partners" />
-          </div>
-        </div> <!-- end .wrap -->
-      </div> <!-- end .landing-section -->
-
-
-      <div class="landing-section" >
-        <div class="wrap">
-          <div class="landing-section-header">
-            <div class="landing-h1">Developer Stories</div>
-          </div>
-
-          <div class="landing-body">
-            <p>We're working with developers to bring many popular apps to Android Auto:</p>
-            <p>
-
-            </p>
-          </div>
-
-        </div> <!-- end .wrap -->
-      </div> <!-- end .landing-section -->
-
-
-      <div class="landing-section" style="background-color:#f5f5f5">
-        <div class="wrap">
-          <div class="landing-section-header">
-            <div class="landing-pre-h1">Coming soon</div>
-            <div class="landing-h1">Android Auto SDK</div>
-            <div style="text-align:center;margin-top:20px;font-size:14pt;margin-bottom:-5px">
-                <a href="https://docs.google.com/a/google.com/forms/d/1ANgYOoYLkfyZ2JRPSU34Nep5yNaU-Ha2syXJ9b4xLrA/viewform">Sign up for updates</a>
+          <div class="cols">
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/abarth.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/alpharomeo.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/acura.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/audi.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/bentley.png"
+                   width="120" height="120" class="img-logo" />
             </div>
           </div>
-
-          <div class="landing-body">
-            <p>In the coming months, we’ll be releasing the Android Auto SDK, which includes APIs
-              and tools to make your existing apps compatible with Android Auto. The first version
-              of the SDK will provide templates and APIs for music, podcast, live radio, and audio
-              news apps, as well as limited voice actions.</p>
-            <p style="margin-bottom:40px">
-              Future versions of the Android Auto SDK will include support for a selection of
-              Android notifications, additional voice actions, and templates and APIs for
-              messaging, communication, local search, and more.</p>
-
-              <a target="_blank" href="http://g.co/androidautodev">
-                <img class="landing-social-image" src="//www.google.com/images/icons/product/gplus-128.png" alt="+Android Auto Developers">
-              </a>
-              <p style="margin-bottom:5px">G+ Community</p>
-              <p class="landing-small">
-                Join the Android Auto developer community on Google+ to stay involved, get the
-                latest updates, and exchange experiences with other developers.
-                <a target="_blank" href="http://g.co/androidautodev">+Android Auto Developers</a>
-              </p>
+          <div class="cols">
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/chevrolet.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/chrysler.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/dodge.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/fiat.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/ford.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
           </div>
-
-        </div> <!-- end .wrap -->
-      </div> <!-- end .landing-section -->
-
+          <div class="cols">
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/honda.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/hyundai.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/infinity.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/jeep.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/kia.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+          </div>
+          <div class="cols">
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/maserati.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/mazda.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/mercedesbenz.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/mitsubishi.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/nissan.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+          </div>
+          <div class="cols">
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/opel.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/ram.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/renault.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/seat.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/skoda.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+          </div>
+          <div class="cols">
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/subaru.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/suzuki.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/volkswagen.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <img src="{@docRoot}auto/images/logos/auto/volvo.png"
+                   width="120" height="120" class="img-logo" />
+            </div>
+            <div class="col-5">
+              <!--<img src="/auto/images/logos/auto/skoda.png"
+                   width="120" height="120" class="img-logo" />-->
+            </div>
+          </div>
+        </div>
+      </div>
     </div> <!-- end .landing-rest-of-page -->
-
-
-    <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
+    <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement"
+    style="border-top: none;">
       <div class="layout-content-col col-16" style="padding-top:4px">
         <style>#___plusone_0 {float:right !important;}</style>
         <div class="g-plusone" data-size="medium"></div>
@@ -205,13 +472,11 @@
         Except as noted, this content is
         licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
         Creative Commons Attribution 2.5</a>. For details and
-        restrictions, see the <a href="/license.html">Content
+        restrictions, see the <a href="{@docRoot}license.html">Content
         License</a>.
       </div>
     </div>
-
-
-  </div> <!-- end landing-body-content -->
+  </div> <!-- end .landing-hero-container -->
 
   <script>
   $("a.landing-down-arrow").on("click", function(e) {
@@ -221,3 +486,5 @@
     e.preventDefault();
   });
   </script>
+
+</div> <!-- end landing-body-content -->
diff --git a/docs/html/auto/overview.jd b/docs/html/auto/overview.jd
index 8748bff..cf63b98 100644
--- a/docs/html/auto/overview.jd
+++ b/docs/html/auto/overview.jd
@@ -13,6 +13,47 @@
 #copyright {
     margin-top:-35px;
 }
+.auto-img-container {
+  position:relative;
+}
+.auto-img-frame {
+  z-index:2;
+  position:relative;
+}
+.auto-img-shot {
+  position:absolute;
+  top:9px;
+  left:8px;
+  z-index:1;
+}
+.auto-img-container-cols {
+  position:relative;
+  margin-top:10px;
+}
+.auto-img-frame-cols {
+  width:380px;
+  z-index:2;
+  position:relative;
+}
+.auto-img-shot-cols {
+  width:369px;
+  position:absolute;
+  top:7px;
+  left:6px;
+  z-index:1;
+}
+.auto-col-2 {
+  width:380px;
+  display: inline;
+  float: left;
+  margin-left: 10px;
+  margin-right: 10px;
+}
+.auto-img-container-single {
+  width:380px;
+  margin:0 auto;
+  margin-top:20px;
+}
 </style>
 
 <div style="width:780px; margin:0 auto;">
@@ -28,13 +69,13 @@
     </ol>
   </li>
   <li><a href="#architecture">Architecture</a></li>
-  <li><a href="#uitemplates">UI Templates</a>
+  <li><a href="#ui">User Interface</a>
     <ol>
       <li><a href="#launchapp">Launch App</a></li>
       <li><a href="#useractions">User Actions</a></li>
       <li><a href="#drawertransitions">Drawer Transitions</a></li>
       <li><a href="#daynighttransitions">Day and Night Transitions</a></li>
-      <li><a href="#customizetemplates">Customizing Templates</a></li>
+      <li><a href="#customizeui">Customizing the UI</a></li>
     </ol>
   </li>
   <li><a href="#devprocess">Development Process</a></li>
@@ -46,27 +87,33 @@
 
 <h1>Android Auto Developer Overview</h1>
 
-<p>Android Auto extends the Android platform to car entertainment systems. When users connect
-their Android handheld device to a compatible vehicle, Android Auto lets users project apps on
-the vehicle’s touchscreen and interact with them using the vehicle’s controls.</p>
+<p>Android Auto extends the Android platform into the car. When users connect
+their Android handheld device to a compatible vehicle, Android Auto provides a car-optimized
+Android experience on the vehicle's screen. Users interact with compatible apps and services
+through voice actions and the vehicle's input controls.</p>
+
+<p>The Android Auto SDK lets you easily extend your existing apps to work in the car, without
+having to worry about vehicle-specific hardware differences. You can use many Android APIs and
+services you are already familiar with. Android Auto provides an easy to use UI model and
+supports notifications and voice actions:</p>
 
 <dl>
-<dt style="margin-bottom:10px"><strong>UI Templates</strong></dt>
+<dt style="margin-bottom:10px"><strong>Media UI</strong></dt>
 <dd style="margin-bottom:20px">
-Android Auto defines interaction models and UI templates for several app categories. The
+Android Auto defines interaction models and car-specific UI patterns for apps. The
 first version of Android Auto supports media apps, such as music, podcast, live radio, and
-audio news apps. Future versions will support messaging, communication, local search apps,
-and more.
+audio news apps.
 </dd>
 <dt style="margin-bottom:10px"><strong>Notifications</strong></dt>
 <dd style="margin-bottom:20px">
 The platform will integrate with existing Android APIs for notifications. Users will get
-some notifications from Android apps on the vehicle’s screen.</dd>
+car appropiate notifications from Android apps on the vehicle's screen.</dd>
 
 <dt style="margin-bottom:10px"><strong>Voice Actions</strong></dt>
 <dd style="margin-bottom:20px">
-Android Auto supports voice search and voice actions for media apps. Future versions
-will support additional voice actions.</dd>
+Android Auto supports a set of voice actions to interact with compatible apps and services.
+Apps can respond to the voice actions they're interested in, such as playing a particular song
+or taking a note.</dd>
 
 <dt style="margin-bottom:10px"><strong>Easy Development Workflow</strong></dt>
 <dd style="margin-bottom:20px">
@@ -79,78 +126,54 @@
 Android Auto experience on a regular Android device.</p>
 
 
-<h2 id="design" style="margin-top:30px">Design</h2>
+<h2 id="design">Design</h2>
 
-<p>Digital experiences for cars should complement and augment driving, not demand the driver's
-attention. Designing these experiences for cars is fundamentally different than in the case of
-phones and tablets. It requires rethinking how these experiences unfold.</p>
+<p>Android Auto extends users' digital ecosystem into their cars, allowing drivers to stay
+connected to their virtual worlds while staying focused on the road ahead.</p>
 
-<h3 id="designprinciples" style="margin-top:25px">Design Principles</h3>
+<p>Because driving is the primary activity in the car, any digital experiences should be designed
+to complement and augment that activity. They should never demand the user's attention.</p>
 
-<p><strong>Glanceable</strong>. We designed Android Auto to reduce UI complexity, optimize user
-interactions, and lower cognitive load. Effective apps show just enough information
-and only provide features that do not require excessive menu interaction and navigation.</p>
+<p>Designing for cars is fundamentally different than designing for phones or tablets, and
+requires rethinking how experiences unfold. Because attention is limited and not all tasks are
+possible in the car, effective apps leverage the entire set of devices that drivers have,
+leveraging the app experience on those devices, outside of the car, to set the stage for simple
+experiences while driving.</p>
+
+<p>Android Auto experiences are:</p>
+
+<p><strong>Glanceable and simple</strong>. Driving requires users' full attention. In-car software
+should not. Android Auto was designed to simplify not only the UI, but to optimize interactions
+and require less thinking, induce lower cognitive load, and ultimately, be safer. Effective apps
+provide just enough information in the minimum amount of time the user needs to glance at it and
+return their attention back to the road. Apps should also reduce the number of features to only
+those that are safe and drive-appropriate.</p>
 
 <p><strong>Predictive, yet predictable</strong>. Android Auto leverages rich, contextual awareness
-to keep the driver informed about important situations. Timely help is combined with predictable
-functions. Effective apps use patterns for common tasks and show timely information only when
-relevant.</p>
+to keep the driver informed about important situations during the drive. Rich, timely help is
+combined with predictable functions. Effective apps make use of the patterns for common tasks and
+show timely information only when relevant.</p>
 
-<p><strong>Connected</strong>. Android Auto works with apps that drivers already use in other
-devices. Android Auto promotes a continuous app experience from phones and tablets to cars,
-providing access to user's existing settings, subscriptions, and digital libraries. Experiences
-that bring personal content and context from other devices are part of Android Auto.</p>
+<p><strong>Connected</strong>. By leveraging the user's personal ecosystem of apps and services,
+Android Auto promotes a continuous experience from phone to car to other devices. The user's
+music, destinations, and virtual ecosystem are always available to augment the drive. Experiences
+that leverage personal context and other devices are naturally part of Android Auto.</p>
 
-<p><strong>Integrated</strong>. Android Auto blends your apps with the vehicle's entertainment
-system, creating a truly integrated experience in every car. By using the vehicle's screen and
-controls, apps feel tailored to each car.</p>
-
-<h3 id="uioverview" style="margin-top:25px">UI Overview</h3>
-
-<p>Android Auto is a new environment that leverages existing UI models where appropiate and adds
-new models based on constrains and context. There are three primary concepts for Android Auto:
-<strong>Suggest</strong>, a unified place for predictive content; <strong>Demand</strong>, a
-pervasive way to interact with voice; and the <strong>Facets</strong>, organized spaces for
-primary activities, apps and content.</p>
-
-<dl>
-<dt style="margin-bottom:10px"><strong>Suggest: The Google Facet</strong></dt>
-<dd style="margin-bottom:20px">
-Core of continuity and extensibility is contextual stream of Now-like cards, powered by your
-apps and notifications. Relevant, timely, and dynamic, the stream organizes likely people, media,
-places, and information so drivers can quickly continue their activities or start something new.
-Google Now and notifications enable drivers to use the apps and services they know when they are
-most relevant.
-</dd>
-<dt style="margin-bottom:10px"><strong>Demand: The Voice Layer</strong></dt>
-<dd style="margin-bottom:20px">
-Voice-enabled tasks lets drivers accomplish their goals without taking their eyes off the road.
-Android Auto defines actions and intents that your app can register for.
-These are accessible through both persistent UI elements and dedicated hardware controls.
-</dd>
-<dt style="margin-bottom:10px"><strong>Facets and Templates: App-powered activities</strong></dt>
-<dd style="margin-bottom:20px">
-In the car, pages of app icons and different UIs create a distracting and dangerous situation.
-Instead, Android Auto apps power templates which help provide simple but customizable UIs
-for common interactions such as media or communications. Templates incorporate common behaviors,
-such as play/pause or reply to a message, while still letting your app promote its value
-and its brand. Apps are organized into facets (or activities) to enable quick access.
-</dd>
-</dl>
-
-<p>Android Auto uses the input and output mechanisms in each vehicle to tailor the interactions.
-Some vehicles have dedicated hardware controls, while others have primarily touch-based systems.
-Android Auto maps common actions and intents across these diverse sets of controls and outputs to
-enable you to concentrate on your unique app experience.</p>
+<p><strong>Naturally integrated</strong>. Android Auto blends the user's apps with the car,
+creating a truly integrated experience that leverages what is unique about each car. By using
+the screens, controls, and capabilities of the vehicle, Android Auto feels like an extension of
+the car.</p>
 
 
-<h2 id="architecture" style="margin-top:30px">Architecture</h2>
 
-<p>The Android Auto app projects your app's customized UI on the vehicle's screen. To communicate
+
+<h2 id="architecture">Architecture</h2>
+
+<p>The Android Auto app shows your app's customized UI on the vehicle's screen. To communicate
 with the Android Auto app, your media app implements a set of media interfaces.</p>
 
 <div style="width:750px;margin:0 auto">
-<img src="/auto/images/figure01.png" alt="" id="figure1" />
+<img src="{@docRoot}auto/images/figure01.png" alt="" />
 <p class="img-caption">
   <strong>Figure 1</strong> - Architecture of Android Auto.
 </p>
@@ -161,7 +184,7 @@
 <p><strong>Media App</strong> - Runs a media service that exposes content through browsing and
 playback APIs. The service provides content to the Android Auto app. This is your Android app.</p>
 
-<p><strong>Android Auto App</strong> - Creates a templated UI and handles user interactions.
+<p><strong>Android Auto App</strong> - Creates the UI and handles user interactions.
 This app uses a media client to request content from the media service running in the media
 app. The client requests data from the media service and monitors service states.</p>
 
@@ -180,104 +203,129 @@
 </ul>
 
 
-<h2 id="uitemplates" style="margin-top:30px">UI Templates</h2>
+<h2 id="ui">User Interface</h2>
 
-<p>The Android Auto app uses a templated UI to display content and user interaction
-opportunities. Android Auto provides you with a set of standard UI templates that follow
-international guidelines for minimizing driving distraction. You do not have to test your
-app's UI for for driver distraction, which is a lengthy and expensive process involving
-multiple legislations across the globe and different standards for each vehicle OEM.</p>
+<p>The Android Auto app uses a car-specific UI model to display content and user interaction
+opportunities. Android Auto provides you with a standard UI designed to minimize driver
+distraction. You do not have to test a custom UI for for driver distraction, which is a
+lengthy and expensive process involving multiple legislations across the globe and different
+standards for each vehicle OEM.</p>
 
-<p>The UI templates define interfaces for browsing, searching, and listening to content from
-media apps. Although you cannot change the standard template format or layout, you can customize
-the template colors, action icons, background images, and more.</p>
+<p>The UI defines interfaces for browsing, searching, and listening to content from
+media apps. You can customize the UI colors, action icons, background images, and more.</p>
 
-<h3 id="launchapp" style="margin-top:25px">Launch App Template</h3>
+<h3 id="launchapp">Launcher</h3>
 
-<p>The Launcher template shows all the compatible media apps installed on the user’s
+<p>The launcher shows all the compatible media apps installed on the user’s
 Android device and lets users select one of them from an scrollable list:</p>
 
-<div style="width:500px;margin:0 auto">
-<img src="/auto/images/figure02.png" alt="" id="figure2" />
-<p class="img-caption">
-  <strong>Figure 2.</strong> The Launcher template.
-</p>
+<div class="auto-img-container-single">
+  <div class="auto-img-container">
+    <img class="auto-img-frame-cols" src="/auto/images/assets/00_frame.png" />
+    <img class="auto-img-shot-cols" src="/auto/images/assets/do_01_switcher.png" />
+  </div>
+  <p class="img-caption" style="margin-top:0px">
+    <strong>Figure 2.</strong> The launcher.
+  </p>
 </div>
 
-<h3 style="margin-top:25px">Primary App Template</h3>
+<h3>Primary App UI</h3>
 
-<p>After the user selects a media app, the display shows the primary app template. Figure
-3 shows the elements of this template that you can customize:</p>
+<p>After the user selects a media app, the display shows the primary app UI.
+You can customize this UI to show your own icons, app name, and
+background images. Figure 3 shows an example of a customized UI:</p>
 
-<div style="width:428px;margin:0 auto">
-<img src="/auto/images/figure03.png" alt="" id="figure3" />
-<p class="img-caption">
-  <strong>Figure 3.</strong> The main application template.
-</p>
+<div class="cols">
+<div class="auto-col-2">
+  <div class="auto-img-container-cols">
+    <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+    <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/do_05_template.png" />
+  </div>
 </div>
-
-<p>You can customize the primary app template to show your own icons, app name, and
-background images. Figure 4 shows an example of a customized template:</p>
-
-<div style="width:787px;margin:0 auto">
-<img src="/auto/images/figure04.png" alt="" id="figure4" />
-<p class="img-caption">
-  <strong>Figure 4.</strong> A customized template.
-</p>
+<div class="auto-col-2">
+  <div class="auto-img-container-cols">
+    <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+    <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/do_02_music.png" />
+  </div>
 </div>
+</div>
+<p class="img-caption">
+  <strong>Figure 3.</strong> A customized UI.
+</p>
 
-<h3 id="useractions" style="margin-top:25px">User Actions</h3>
 
-<p>The primary app template supports four main actions on the action bar, four auxiliary actions
+
+<h3 id="useractions">User Actions</h3>
+
+<p>The primary app UI supports four main actions on the action bar, four auxiliary actions
 on the overflow bar, and the <em>Return</em> action. You can use standard controls and customize
-the actions and icons, as shown in Figure 5.</p>
+the actions and icons, as shown in Figure 4.</p>
 
-<div style="width:500px;margin:0 auto">
-<img src="/auto/images/figure05.png" alt="" id="figure5" />
-<p class="img-caption">
-  <strong>Figure 5.</strong> Custom icons for auxiliary actions.
-</p>
+<div class="auto-img-container-single">
+  <div class="auto-img-container">
+    <img class="auto-img-frame-cols" src="/auto/images/assets/00_frame.png" />
+    <img class="auto-img-shot-cols" src="/auto/images/assets/do_03_more.png" />
+  </div>
+  <p class="img-caption" style="margin-top:0px">
+    <strong>Figure 4.</strong> Custom extra actions.
+  </p>
 </div>
 
-<h3 id="drawertransitions" style="margin-top:25px">Drawer Transitions</h3>
+<h3 id="drawertransitions">Drawer Transitions</h3>
 
-<p>For browse actions, the display shows the drawer transition and template:</p>
+<p>For browse actions, the display shows the drawer transition as shown in Figure 5.</p>
 
-<div style="width:750px;margin:0 auto">
-<img src="/auto/images/figure06.png" alt="" id="figure6" />
-<p class="img-caption">
-  <strong>Figure 6.</strong> The drawer transition.
-</p>
+<div class="cols">
+<div class="auto-col-2">
+  <div class="auto-img-container-cols">
+    <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+    <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/do_06_gdrawer.png" />
+  </div>
 </div>
+<div class="auto-col-2">
+  <div class="auto-img-container-cols">
+    <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+    <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/do_04_mdrawer.png" />
+  </div>
+</div>
+</div>
+<p class="img-caption">
+  <strong>Figure 5.</strong> Generic and customized drawers.
+</p>
 
-<p>After the transition from the primary app template to the drawer template, the drawer
-appears on the center. The customized drawer template shows the media containers and
+<p>After the transition from the primary app UI to the drawer UI, the drawer
+appears on the center. The customized drawer UI shows the media containers and
 media files provided by the media service in your app. You can also customize drawers
 with icons for list items.</p>
 
-<div style="width:500px;margin:0 auto">
-<img src="/auto/images/figure07.png" alt="" id="figure7" />
-<p class="img-caption">
-  <strong>Figure 7.</strong> A customized drawer template.
-</p>
+
+<h3 id="daynighttransitions">Day and Night Transitions</h3>
+
+<p>All the UIs support different color schemes for day and night.
+The platform provides the state (day or night) and makes adjustments automatically.</p>
+
+<div class="cols">
+<div class="auto-col-2">
+  <div class="auto-img-container-cols">
+    <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+    <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/do_02_music.png" />
+  </div>
 </div>
-
-<h3 id="daynighttransitions" style="margin-top:25px">Day and Night Transitions</h3>
-
-<p>All the templates support different color schemes for day and night, as shown in
-Figure 8. The platform provides the state (day or night) and makes adjustments automatically.</p>
-
-<div style="width:780px;margin:0 auto">
-<img src="/auto/images/figure08.png" alt="" id="figure8" />
-<p class="img-caption">
-  <strong>Figure 8.</strong> Day and night transitions.
-</p>
+<div class="auto-col-2">
+  <div class="auto-img-container-cols">
+    <img class="auto-img-frame-cols" src="{@docRoot}auto/images/assets/00_frame.png" />
+    <img class="auto-img-shot-cols" src="{@docRoot}auto/images/assets/do_02_music_night.png" />
+  </div>
 </div>
+</div>
+<p class="img-caption">
+  <strong>Figure 6.</strong> Day and night modes.
+</p>
 
-<h3 id="customizetemplates" style="margin-top:25px">Customizing Templates</h3>
+<h3 id="customizeui">Customizing UIs</h3>
 
-<p>To customize the templates, provide the following app-specific resources and actions
-to the Android Auto media client.</p>
+<p>To customize the UI, you provide the following app-specific resources and actions
+to the Android Auto media client:</p>
 
 <ul>
 <li><strong>Resources</strong> - App logo, app name, theme colors, and background images.</li>
@@ -285,10 +333,10 @@
 <em>Favorite</em>, and <em>Bookmark</em>. These actions are app-specific.</li>
 </ul>
 
-<p>If provided, the media client automatically uses them in the templated UI.</p>
+<p>If provided, the media client automatically uses them in the UI.</p>
 
 
-<h2 id="devprocess" style="margin-top:30px">Development Process</h2>
+<h2 id="devprocess">Development Process</h2>
 
 <p class="note"><strong>Note:</strong> When released, the Android Auto SDK will provide
 media service interfaces, an APK for handheld devices that simulates the Android Auto
@@ -297,10 +345,10 @@
 <p>To create a media app for Android Auto, you include an Android service in your app
 that implements the media service interfaces provided by the Android Auto SDK. These
 interfaces define functionality for browsing and finding content, playing media,
-customizing the UI template, and performing app-specific actions.</p>
+customizing the UI, and performing app-specific actions.</p>
 
 <p>The media service interfaces present the content library as a navigable tree and enable
-clients to play media, get album art, obtain theme resources for the UI template, and
+clients to play media, get album art, obtain theme resources for the UI, and
 invoke app-specific actions.</p>
 
 <p>You don’t have to create a new app for Android Auto: you can extend your existing
@@ -327,7 +375,7 @@
 controls.</p>
 
 
-<h2 id="emulator" style="margin-top:30px">Testing Your App on an Android Device</h2>
+<h2 id="emulator">Testing Your App on an Android Device</h2>
 
 <p>The Android Auto SDK includes an APK with a media client implementation, which is
 similar to those available in compatible vehicles. To test your app with this
@@ -348,7 +396,7 @@
 in the UI controls.</p>
 
 
-<h2 id="running" style="margin-top:30px">Running Your App on Android Auto</h2>
+<h2 id="running">Running Your App on Android Auto</h2>
 
 <p>Media apps are available on the Google Play Store for compatible Android devices.
 When users connect their Android device to a compatible vehicle, the
diff --git a/docs/html/design/auto/index.jd b/docs/html/design/auto/index.jd
new file mode 100644
index 0000000..aac79ab
--- /dev/null
+++ b/docs/html/design/auto/index.jd
@@ -0,0 +1,35 @@
+page.title=Auto
+@jd:body
+
+<style>
+.auto-img-container-cols {
+  position:relative;
+  margin-bottom:25px;
+  margin-top:25px;
+}
+.auto-img-frame-cols {
+  z-index:2;
+  position:relative;
+}
+.auto-img-shot-cols {
+  position:absolute;
+  top:5px;
+  left:2px;
+  z-index:1;
+}
+</style>
+
+<div class="auto-img-container-cols" style="float:right; margin:0 0 40px 40px;width:460px">
+  <img class="auto-img-frame-cols" src="/auto/images/assets/00_frame.png">
+  <img class="auto-img-shot-cols" src="/auto/images/assets/03_a_musict.png">
+</div>
+
+<p>Android Auto is <strong>coming soon</strong> and brings apps to the car,
+integrating with the vehicle's input controls and display.</p>
+
+<p>The future design guidelines provide templates that define the user interaction model for all apps and let you hook into a standard UI with touch and voice controls. The templates meet international best practices for reducing driver distraction while still letting you customize and brand them to properly deliver your content.</p>
+
+<p><a href="{@docRoot}auto/index.html">Learn more about Android Auto</a>.</p>
+
+
+
diff --git a/docs/html/design/design_toc.cs b/docs/html/design/design_toc.cs
index 2bd0bf9..885f336 100644
--- a/docs/html/design/design_toc.cs
+++ b/docs/html/design/design_toc.cs
@@ -5,7 +5,26 @@
     <ul>
       <li><a href="<?cs var:toroot ?>design/get-started/creative-vision.html">Creative Vision</a></li>
       <li><a href="<?cs var:toroot ?>design/get-started/principles.html">Design Principles</a></li>
-      <li><a href="<?cs var:toroot ?>design/get-started/ui-overview.html">UI Overview</a></li>
+    </ul>
+  </li>
+
+  <li class="nav-section">
+    <div class="nav-section-header"><a href="<?cs var:toroot ?>design/devices.html">Devices</a></div>
+    <ul>
+      <li><a href="<?cs var:toroot ?>design/handhelds/index.html">Phones &amp; Tablets</a></li>
+      <li class="nav-section">
+        <div class="nav-section-header">
+          <a href="<?cs var:toroot ?>design/wear/index.html">Wear</a></div>
+        <ul>
+          <li><a href="<?cs var:toroot ?>design/wear/creative-vision.html">Creative Vision</a></li>
+          <li><a href="<?cs var:toroot ?>design/wear/principles.html">Design Principles</a></li>
+          <li><a href="<?cs var:toroot ?>design/wear/structure.html">App Structure</a></li>
+          <li><a href="<?cs var:toroot ?>design/wear/patterns.html">UI Patterns</a></li>
+          <li><a href="<?cs var:toroot ?>design/wear/style.html">Style</a></li>
+        </ul>
+      </li>
+      <li><a href="<?cs var:toroot ?>design/tv/index.html">TV</a></li>
+      <li><a href="<?cs var:toroot ?>design/auto/index.html">Auto</a></li>
     </ul>
   </li>
 
@@ -67,25 +86,6 @@
   </li>
 
   <li class="nav-section">
-    <div class="nav-section-header"><a href="<?cs var:toroot ?>design/devices.html">Devices</a></div>
-    <ul>
-      <!-- wear design goes here -->
-      <li class="nav-section">
-        <div class="nav-section-header">
-          <a href="<?cs var:toroot ?>design/tv/index.html">TV</a></div>
-        <ul>
-          <li><a href="<?cs var:toroot ?>design/tv/principles.html">Design Principles</a></li>
-          <li><a href="<?cs var:toroot ?>design/tv/ui-overview.html">UI Overview</a></li>
-          <li><a href="<?cs var:toroot ?>design/tv/style.html">Style</a></li>
-          <li><a href="<?cs var:toroot ?>design/tv/patterns.html">Patterns</a></li>
-        </ul>
-      </li>
-
-    </ul>
-  </li>
-
-
-  <li class="nav-section">
     <div class="nav-section-header empty"><a href="<?cs var:toroot ?>design/downloads/index.html">Downloads</a></div>
   </li>
 
diff --git a/docs/html/design/devices.jd b/docs/html/design/devices.jd
new file mode 100644
index 0000000..0015d01
--- /dev/null
+++ b/docs/html/design/devices.jd
@@ -0,0 +1,37 @@
+page.title=Devices
+page.viewport_width=970
+section.landing=true
+header.hide=1
+footer.hide=1
+@jd:body
+
+<style>
+#landing-graphic-container {
+  position: relative;
+}
+
+#text-overlay {
+  position: absolute;
+  left: 0;
+  top: 420px;
+  width: 360px;
+
+}
+#hero-image {
+}
+</style>
+
+<div id="landing-graphic-container">
+  <div id="text-overlay">
+    <p itemprop="description">The device-centric UI principles, overviews, and detailed guidelines
+      described here build on the core <a href="{@docRoot}design/get-started/principles.html">Android Design Principles</a>
+      to provide more specific design guidance for different form factors.
+    </p>
+    <p>
+    <a href="{@docRoot}design/handhelds/index.html" class="landing-page-link">Phones &amp; Tablets</a></p>
+  </div>
+  <a id="hero-image" href="{@docRoot}design/handhelds/index.html">
+    <img src="{@docRoot}design/media/device_family.png">
+  </a>
+</div>
+
diff --git a/docs/html/design/get-started/creative-vision.jd b/docs/html/design/get-started/creative-vision.jd
index 1ce305a..9261c6e 100644
--- a/docs/html/design/get-started/creative-vision.jd
+++ b/docs/html/design/get-started/creative-vision.jd
@@ -6,10 +6,10 @@
 <div class="vspace size-1">&nbsp;</div>
 
 <p itemprop="description">
-  We focused the design of Android around three overarching goals, which apply
-  to our core apps as well as the system at large. As you design apps to work
-  with Android, consider these goals: <em>Enchant me</em>, <em>Simplify my
-  life</em>, and <em>Make me amazing</em>
+  Starting with Ice Cream Sandwich, we focused the design of
+  Android around these three overarching goals, which apply
+  to our core apps as well as the system at large.
+  As you work with Android, consider these goals.
 </p>
 
 <div class="vspace size-1">&nbsp;</div>
diff --git a/docs/html/design/get-started/principles.jd b/docs/html/design/get-started/principles.jd
index 0b7147b..73ec3a6 100644
--- a/docs/html/design/get-started/principles.jd
+++ b/docs/html/design/get-started/principles.jd
@@ -1,9 +1,16 @@
-page.title=Design Principles
+page.title=Android Design Principles
 @jd:body
 
-<p>These design principles were developed by and for the Android User Experience Team to keep users'
-best interests in mind. Consider them as you apply your own creativity and design thinking. Deviate
-with purpose.</p>
+<p>These design principles were developed by and for the Android
+User Experience Team to keep users' best interests in mind.
+For Android developers and designers, they continue to
+underlie the more  detailed design guidelines for different
+types of devices.</p>
+
+<p>
+Consider these principles as you apply your own
+creativity and design thinking. Deviate with purpose.
+</p>
 
 <h2 id="enchant-me">Enchant Me</h2>
 
diff --git a/docs/html/design/get-started/ui-overview.jd b/docs/html/design/handhelds/index.jd
similarity index 87%
rename from docs/html/design/get-started/ui-overview.jd
rename to docs/html/design/handhelds/index.jd
index 5f4c40f..882b070 100644
--- a/docs/html/design/get-started/ui-overview.jd
+++ b/docs/html/design/handhelds/index.jd
@@ -1,12 +1,20 @@
-page.title=UI Overview
+page.title=Phones &amp; Tablets
 @jd:body
 
-<p>Android's system UI provides the framework on top of which you build your app. Important aspects
-include the Home screen experience, global device navigation, and notifications.</p>
-<p>Your app will play an important part in keeping the overall Android experience consistent and
-enjoyable to use. At the end of this chapter we introduce the main elements for achieving this goal
-in your app.</p>
-<p>Read on for a quick overview of the most important aspects of the Android user interface.</p>
+<p>
+Android's system UI provides the framework on top of which you build your app,
+whether you're designing for phones, tablets, watches, or other form factors.
+Aspects of UI that are especially important for phones and tablets include
+the Home screen experience, global device navigation, and notifications.
+</p>
+
+<p>
+Your app will play an important part in keeping the overall Android experience
+consistent and enjoyable to use. This page introduces some of the main elements
+that can help you achieve this goal. The main Android Design topics listed on
+the left, after the Devices sections, provide detailed guidelines for phones
+and tablets.
+</p>
 
 <h2 id="home-all-apps-recents">Home, All Apps, and Recents</h2>
 
diff --git a/docs/html/design/index.jd b/docs/html/design/index.jd
index cb7dd4f..27e3169 100644
--- a/docs/html/design/index.jd
+++ b/docs/html/design/index.jd
@@ -13,7 +13,7 @@
 
 #text-overlay {
   position: absolute;
-  left: 36px;
+  left: 0;
   top: 42px;
   width: 266px;
 
@@ -34,5 +34,15 @@
   <a id="hero-image" href="/design/get-started/creative-vision.html">
     <img src="/design/media/index_landing_page.png">
   </a>
+
+<div style="background: hsl(8, 70%, 54%); margin: 0; padding: 20px 20px 10px 20px;color: #fff; position: absolute;top: 255px;width: 179px;">
+<h2 style="color: #fff;margin:0 0 10px; font-size:18px" class="norule">L Developer Preview</h2>
+<p> The next version of Android uses a design
+metaphor inspired by paper and ink that provides a reassuring sense of tactility. Before it arrives for users, you can get an early
+look at the new Material design.
+</p>
+<p><a class="white" href="{@docRoot}preview/material/index.html">Learn more about Material</a></p>
+</div>
+
 </div>
 
diff --git a/docs/html/design/media/device_family.png b/docs/html/design/media/device_family.png
new file mode 100644
index 0000000..7889884
--- /dev/null
+++ b/docs/html/design/media/device_family.png
Binary files differ
diff --git a/docs/html/design/media/wear/1D_picker.png b/docs/html/design/media/wear/1D_picker.png
new file mode 100644
index 0000000..46c6bf6
--- /dev/null
+++ b/docs/html/design/media/wear/1D_picker.png
Binary files differ
diff --git a/docs/html/design/media/wear/2D_picker.png b/docs/html/design/media/wear/2D_picker.png
new file mode 100644
index 0000000..82c766a
--- /dev/null
+++ b/docs/html/design/media/wear/2D_picker.png
Binary files differ
diff --git a/docs/html/design/media/wear/2D_picker_action.png b/docs/html/design/media/wear/2D_picker_action.png
new file mode 100644
index 0000000..8560ef8
--- /dev/null
+++ b/docs/html/design/media/wear/2D_picker_action.png
Binary files differ
diff --git a/docs/html/design/media/wear/Bluebird.png b/docs/html/design/media/wear/Bluebird.png
new file mode 100644
index 0000000..447e643
--- /dev/null
+++ b/docs/html/design/media/wear/Bluebird.png
Binary files differ
diff --git a/docs/html/design/media/wear/action_button.png b/docs/html/design/media/wear/action_button.png
new file mode 100644
index 0000000..dfdffa3
--- /dev/null
+++ b/docs/html/design/media/wear/action_button.png
Binary files differ
diff --git a/docs/html/design/media/wear/action_on_card.png b/docs/html/design/media/wear/action_on_card.png
new file mode 100644
index 0000000..d0b0fff
--- /dev/null
+++ b/docs/html/design/media/wear/action_on_card.png
Binary files differ
diff --git a/docs/html/design/media/wear/assets_specifics.png b/docs/html/design/media/wear/assets_specifics.png
new file mode 100644
index 0000000..35a3819
--- /dev/null
+++ b/docs/html/design/media/wear/assets_specifics.png
Binary files differ
diff --git a/docs/html/design/media/wear/bridgednotifications.jpg b/docs/html/design/media/wear/bridgednotifications.jpg
new file mode 100644
index 0000000..a9e57a4
--- /dev/null
+++ b/docs/html/design/media/wear/bridgednotifications.jpg
Binary files differ
diff --git a/docs/html/wear/images/circle_message2.png b/docs/html/design/media/wear/circle_message2.png
similarity index 100%
rename from docs/html/wear/images/circle_message2.png
rename to docs/html/design/media/wear/circle_message2.png
Binary files differ
diff --git a/docs/html/design/media/wear/clear_bold_type.jpg b/docs/html/design/media/wear/clear_bold_type.jpg
new file mode 100644
index 0000000..e4b742c
--- /dev/null
+++ b/docs/html/design/media/wear/clear_bold_type.jpg
Binary files differ
diff --git a/docs/html/design/media/wear/confirmation.png b/docs/html/design/media/wear/confirmation.png
new file mode 100644
index 0000000..513b85f
--- /dev/null
+++ b/docs/html/design/media/wear/confirmation.png
Binary files differ
diff --git a/docs/html/design/media/wear/contextualnotification.png b/docs/html/design/media/wear/contextualnotification.png
new file mode 100644
index 0000000..1ec7ac8
--- /dev/null
+++ b/docs/html/design/media/wear/contextualnotification.png
Binary files differ
diff --git a/docs/html/design/media/wear/continue_phone.png b/docs/html/design/media/wear/continue_phone.png
new file mode 100644
index 0000000..fed93b6
--- /dev/null
+++ b/docs/html/design/media/wear/continue_phone.png
Binary files differ
diff --git a/docs/html/design/media/wear/copywrite.png b/docs/html/design/media/wear/copywrite.png
new file mode 100644
index 0000000..78be0bd
--- /dev/null
+++ b/docs/html/design/media/wear/copywrite.png
Binary files differ
diff --git a/docs/html/design/media/wear/countdown.png b/docs/html/design/media/wear/countdown.png
new file mode 100644
index 0000000..11b1504
--- /dev/null
+++ b/docs/html/design/media/wear/countdown.png
Binary files differ
diff --git a/docs/html/design/media/wear/customlayout.jpg b/docs/html/design/media/wear/customlayout.jpg
new file mode 100644
index 0000000..9573cfc
--- /dev/null
+++ b/docs/html/design/media/wear/customlayout.jpg
Binary files differ
diff --git a/docs/html/design/media/wear/dismiss_cards.png b/docs/html/design/media/wear/dismiss_cards.png
new file mode 100644
index 0000000..2e2d53b8
--- /dev/null
+++ b/docs/html/design/media/wear/dismiss_cards.png
Binary files differ
diff --git a/docs/html/design/media/wear/expandable_stacks.png b/docs/html/design/media/wear/expandable_stacks.png
new file mode 100644
index 0000000..edc2456
--- /dev/null
+++ b/docs/html/design/media/wear/expandable_stacks.png
Binary files differ
diff --git a/docs/html/wear/images/fitness-24.png b/docs/html/design/media/wear/fitness.png
similarity index 100%
rename from docs/html/wear/images/fitness-24.png
rename to docs/html/design/media/wear/fitness.png
Binary files differ
diff --git a/docs/html/design/media/wear/low_info_card.png b/docs/html/design/media/wear/low_info_card.png
new file mode 100644
index 0000000..a3ebf16
--- /dev/null
+++ b/docs/html/design/media/wear/low_info_card.png
Binary files differ
diff --git a/docs/html/design/media/wear/peek_card.png b/docs/html/design/media/wear/peek_card.png
new file mode 100644
index 0000000..2b12297
--- /dev/null
+++ b/docs/html/design/media/wear/peek_card.png
Binary files differ
diff --git a/docs/html/design/media/wear/selection_list.png b/docs/html/design/media/wear/selection_list.png
new file mode 100644
index 0000000..dcb0745
--- /dev/null
+++ b/docs/html/design/media/wear/selection_list.png
Binary files differ
diff --git a/docs/html/design/media/wear/separate_info_cards.jpg b/docs/html/design/media/wear/separate_info_cards.jpg
new file mode 100644
index 0000000..d4cb386
--- /dev/null
+++ b/docs/html/design/media/wear/separate_info_cards.jpg
Binary files differ
diff --git a/docs/html/design/media/wear/separate_info_cards_1.jpg b/docs/html/design/media/wear/separate_info_cards_1.jpg
new file mode 100644
index 0000000..b987aea
--- /dev/null
+++ b/docs/html/design/media/wear/separate_info_cards_1.jpg
Binary files differ
diff --git a/docs/html/design/media/wear/separate_info_cards_2.jpg b/docs/html/design/media/wear/separate_info_cards_2.jpg
new file mode 100644
index 0000000..1930cb8
--- /dev/null
+++ b/docs/html/design/media/wear/separate_info_cards_2.jpg
Binary files differ
diff --git a/docs/html/design/media/wear/single_action_controls.jpg b/docs/html/design/media/wear/single_action_controls.jpg
new file mode 100644
index 0000000..ef89da0
--- /dev/null
+++ b/docs/html/design/media/wear/single_action_controls.jpg
Binary files differ
diff --git a/docs/html/design/media/wear/voice_commands.png b/docs/html/design/media/wear/voice_commands.png
new file mode 100644
index 0000000..9839ed8
--- /dev/null
+++ b/docs/html/design/media/wear/voice_commands.png
Binary files differ
diff --git a/docs/html/design/tv/index.jd b/docs/html/design/tv/index.jd
index 2519e25..5534724 100644
--- a/docs/html/design/tv/index.jd
+++ b/docs/html/design/tv/index.jd
@@ -1,31 +1,15 @@
-page.title=Design for TV
-header.justLinks=1
-footer.hide=1
+page.title=TV
 @jd:body
 
-<style>
-#landing-graphic-container {
-  position: relative;
-}
 
-#text-overlay {
-  position: absolute;
-  left: 0;
-  top: 402px;
-  width: 220px;
-}
-</style>
+<img src="{@docRoot}preview/tv/design/images/atv-home.jpg"
+  width="460" height="283" style="float:right;margin:0 0 40px 40px" /> 
 
-<div id="landing-graphic-container">
-  <div id="text-overlay">
-    <span itemprop="description">
-      Build beautiful apps for the biggest screen in the house.</span>
-    <br><br>
-    <a href="{@docRoot}design/tv/principles.html"
-       class="landing-page-link">Design Principles</a>
-  </div>
 
-  <a href="{@docRoot}design/tv/principles.html">
-    <img src="{@docRoot}design/tv/images/atv.png" style="margin-left: 70px;">
-  </a>
-</div>
+<p>Android TV is <strong>coming soon</strong> and lets you engage your users in a new, shared environment.</p>
+
+<p>Users bring a specific set of expectations to the experience of watching TV, versus interacting
+with a phone or tablet. So find out how to get your app ready for its big-screen debut
+later this year by reading the
+<a href="{@docRoot}preview/tv/design/index.html">Android TV Design Guide</a>
+in the L Developer Preview.</p>
\ No newline at end of file
diff --git a/docs/html/design/wear/creative-vision.jd b/docs/html/design/wear/creative-vision.jd
new file mode 100644
index 0000000..4530744
--- /dev/null
+++ b/docs/html/design/wear/creative-vision.jd
@@ -0,0 +1,36 @@
+page.title=Creative Vision for Wear
+@jd:body
+
+
+<p>Android Wear devices provide just the right information at just the right time,
+allowing users to be more connected to both the virtual world and the real world. Great Android
+Wear experiences are:</p>
+
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+    <h4>Launched automatically</h4>
+    <p>Most people are used to launching apps by clicking an icon. Android Wear is different. Wearable apps are aware of the user’s context - time, location, physical activity, and so on. The apps use this information to insert cards into the stream when they become relevant. This makes Android Wear timely, relevant and very specific.</p>
+  </div>
+  <div class="layout-content-col span-6" style="margin-left:75px">
+  <h4>Glanceable</h4>
+  <p>A classic wrist watch is designed to let you see the time in a split second and get on with what you were doing. Designing for Android Wear is no different. The less time it takes to use your software, the more time the user can be present in whatever they are doing. Android wear is fast, sharp and immediate.</p>
+  </div>
+</div>
+
+
+<div class="layout-content-row">
+  <div class="layout-content-col span-6">
+  <h4>Zero or low interaction</h4>
+  <p>Staying true to the strengths afforded by a smaller form factor, Android Wear focuses on simple interactions, only requiring input by the user when absolutely necessary. Most inputs are based around touch swipes or voice, and inputs requiring fine-grained finger movements are avoided. Android Wear is gestural, simple, and fast.</p>
+  </div>
+  <div class="layout-content-col span-6" style="margin-left:75px">
+  <h4>All about suggest and demand</h4>
+  <p>Android Wear is like a great personal assistant: it knows you and your preferences, it only interrupts you when absolutely necessary, and it’s always on hand to provide a ready answer. Android Wear is helpful, respectful, and responsive.</p>
+  </div>
+</div>
+
+<p>By providing a smart connection to the rest of the world while respecting the user’s attention, Android Wear feels personal and global, simple and smart, unobtrusive and ever-ready. Applications that represent these principles will feel most at home in the overall Android Wear experience.</p>
+
+<p>Third party apps extend Android Wear to be more specialized and helpful throughout the day. Installing apps are a way for the user to tell the Android Wear how to do that.</p>
+
diff --git a/docs/html/design/wear/index.jd b/docs/html/design/wear/index.jd
new file mode 100644
index 0000000..3536445
--- /dev/null
+++ b/docs/html/design/wear/index.jd
@@ -0,0 +1,67 @@
+page.title=Wear
+@jd:body
+
+
+
+<p>Designing apps for wearables powered by Android Wear
+is substantially different than designing for phones or
+tablets: different strengths and weaknesses, different use cases, different ergonomics.
+To get started, you should understand the overall vision for the Android Wear experience,
+and how apps fit into and enhance this experience.</p>
+
+<p>A new form factor deserves a new UI model. At a high level, the Android Wear UI consists of two
+main spaces centered around the core functions of <strong>Suggest</strong> and
+<strong>Demand</strong>. Your app will have an important role to play in both of these
+spaces.</p>
+
+
+
+<h2 id="Stream">Suggest: The Context Stream</h2>
+
+<div class="framed-wear-square" style="float:right;margin:0 -22px 60px 40px">
+  <img src="{@docRoot}wear/images/screens/stream.gif">
+</div>
+
+<p>The context stream is a vertical list of cards, each showing a useful or timely piece of information. Much like the Google Now feature on Android phones and tablets, users swipe vertically to navigate from card to card. Only one card is displayed at a time, and background photos are used to provide additional visual information. Your application can create cards and inject them into the stream when they are most likely to be useful.</p>
+
+<p>This UI model ensures that users don’t have to launch many different applications to check for updates; they can simply glance at their stream for a brief update on what’s important to them.</p>
+
+<p>Cards in the stream are more than simple notifications. They can be swiped horizontally to reveal additional pages. Further horizontal swiping may reveal buttons, allowing the user to take action on the notification. Cards can also be dismissed by swiping left to right, removing them from the stream until the next time the app has useful information to display.</p>
+
+
+
+
+<h2 id="CueCard">Demand: The Cue Card</h2>
+
+<div class="framed-wear-square" style="float:right;margin:0 -22px 60px 40px">
+  <img src="{@docRoot}wear/images/screens/cuecard.gif">
+</div>
+
+<p>For cases where Android Wear does not suggest an answer proactively through the context stream, the cue card allows users to speak to Google. The cue card is opened by saying, “OK Google” or by tapping on the background of the home screen. Swiping up on the cue card shows a list of suggested voice commands, which can also be tapped.</p>
+
+<p>At a technical level, each suggested voice command activates a specific type of intent. As a developer, you can match your applications to some of these intents so that users can complete tasks using these voice commands. Multiple applications may register for a single voice intent, and the user will have the opportunity to choose which application they prefer to use.</p>
+
+<p>Applications can respond to a voice command in the same way as they can respond to a tap on a regular in-stream action button: by adding or updating a stream card, or by launching a full screen application. Voice input often takes the form of a command, such as "remind me to get milk," in which case a simple confirmation animation is sufficient to display before automatically returning to the Context Stream.</p>
+
+
+<h2 id="Other">Other UI Features</h2>
+
+<ul>
+<li>The <strong>Home screen</strong> is the default state of the device and features:
+  <ul>
+  <li>The background, showing either content relating to the first card or a custom watch face design, depending on the watch face the user has chosen. Tapping anywhere on the background or saying "Ok Google" starts a voice query.
+  <li>Status indicators, showing connectivity, charging status, airplane mode, and in some watch faces a count of unread items.
+  <li>The top ranked card in the Context Stream, peeking up at the bottom of the screen. The amount of the peek card that appears is determined by the current watch face.
+  </ul>
+</li>
+
+<li><strong>Watch faces</strong> may be chosen by the user to appear in the background of the Home screen. Watch faces display the time and accommodate the top ranked peek card. The user can choose a different watch face by long pressing on the current one.</li>
+
+<li>Some devices may enter a low-power <strong>Ambient Mode</strong> when not being used. This usually involves dimming the screen in some way. The contents of a peek card will automatically be optimized for display in this state. Users can exit ambient mode by tapping on the screen, by tilting the screen towards them, or by pressing a hardware button if one exists.</li>
+
+<li>Swiping down on the Home screen reveals the <strong>Date and Battery</strong> display. Dragging further down toggles <strong>Mute mode</strong>, preventing interruptive notifications from vibrating and illuminating the screen.</li>
+
+<li>The <strong>Settings screen</strong> can be invoked from the cue card or on some devices using a hardware button. From here the user may shut down or restart their device, adjust screen brightness, toggle airplane mode, and access device information.</li>
+
+<li><strong>Full screen apps</strong> can be launched on top of the main stream where a wider range of interaction is called for. Although not stylistically limited to the context stream pattern, apps should respect the same design principles as the rest of the system. For more information, see the <a href="{@docRoot}design/wear/structure.html">App Structure</a> guide.</li>
+</ul>
\ No newline at end of file
diff --git a/docs/html/design/wear/patterns.jd b/docs/html/design/wear/patterns.jd
new file mode 100644
index 0000000..8f4698c
--- /dev/null
+++ b/docs/html/design/wear/patterns.jd
@@ -0,0 +1,150 @@
+page.title=UI Patterns for Wear
+@jd:body
+
+
+
+<p>Android Wear is used for micro-interactions, and so adhering to consistent design patterns that users are already accustomed to is paramount.</p>
+
+<h2>Cards</h2>
+
+<p>Cards in the stream can take slightly different forms:</p>
+
+<div class="framed-wear-square-small" style="float:left;margin:0 20px 20px 0">
+  <img src="{@docRoot}design/media/wear/Bluebird.png">
+</div>
+
+<div class="framed-wear-square-small" style="float:left;margin:0 20px 20px 0">
+  <img src="{@docRoot}design/media/wear/single_action_controls.jpg">
+</div>
+
+<div class="framed-wear-square-small" style="float:left;margin:0 20px 20px 0">
+  <img src="{@docRoot}design/media/wear/expandable_stacks.png">
+</div>
+                
+<ul style="clear:both">
+<li>Standard cards for displaying information from a notification</li>
+<li>Single-action controls (such as a play/pause toggle)</li>
+<li>Expandable stack of cards, for grouping a set of related notifications together</li>
+</ul>
+
+
+<h2>App icons</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 20px 60px">
+  <img src="{@docRoot}design/media/wear/clear_bold_type.jpg">
+</div>
+
+<p>App icons appear in a fixed position overhanging the edge at the top right of the card by default for all notifications in the Context Stream. This is an opportunity for cards to be recognized as coming from a specific source. Photo backgrounds should be used only to convey information, not to brand a card. App icons are necessary only on the leftmost card; it is not necessary to add the app icon to pages.</p>
+
+<h2 style="clear:both">Pages</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 20px 40px">
+  <img src="{@docRoot}design/media/wear/separate_info_cards_2.jpg">
+</div>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 20px 40px">
+  <img src="{@docRoot}design/media/wear/separate_info_cards_1.jpg">
+</div>
+
+<p>Supplementary information should be displayed on additional cards to the right of a main Context Stream card. In most cases one additional detail card should be sufficient. For example, a weather card might show the weather for the current location today, with subsequent days listed in an additional card to the right. Keep the number of detail cards as low as possible. Actions (see below) should always come after pages; don’t change the order or interleave them.</p>
+
+
+<h2 style="clear:both">Dismissing cards</h2>
+
+  <img src="{@docRoot}design/media/wear/dismiss_cards.png" height="147">
+
+<p>Swiping from left to right on a card causes it to be dismissed from the stream. Dismissed cards may return when they next have relevant information. State is synced between the Android Wear context stream and the notifications on the Android handheld device, so dismissing from one causes an automatic dismissal from the other.</p>
+
+
+
+<h2 style="clear:both">Action buttons</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 20px 40px">
+  <img src="{@docRoot}design/media/wear/action_button.png">
+</div>
+
+<p>Where the user may need to take action on the information shown in a notification, you can provide action buttons. These are system-rendered buttons that appear to the right of detail cards. They consist of a white icon set on a blue system-rendered circular button and a short caption with a verb. Actions should be limited to three for a single card row.</p>
+
+<p>Tapping on an action button can cause an action to be executed; or an action to be continued on the companion handheld; or a full screen activity to be invoked for further input (see “2D Picker” section below).</p>
+
+<p>Refer to the UI Toolkit provided in the Downloads section for detailed specs regarding action icons.</p>
+
+
+<h2 style="clear:both">Action countdown and confirmation</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 20px 40px">
+  <img src="{@docRoot}design/media/wear/countdown.png">
+</div>
+
+<p>Where tapping on an action button results in an action being executed, one of the following can happen:</p>
+
+<ol>
+<li>The action is completed immediately and the result of the action is shown (either by updating the relevant card contents immediately, or by showing a confirmation animation).</li>
+<li>A short countdown animation to completing the action is played, which the user can interrupt to cancel. Once the timer has counted down, a confirmation animation is played. This animation can be custom-designed by developers.</li>
+<li>A confirmation step is required. This is for actions that are potentially damaging if accidentally triggered. A generic confirmation template is supplied by the system. Once the user confirms, the standard confirmation animation is played.</li>
+<li>The cue card can be invoked to continue specifying the action. For example in a messaging application, tapping a “Reply” action button invokes the Cue Card and prompts for voice input. In this case the prompt label (such as “Speak your message…”) and a set of sample voice suggestions can be specified by developers.</li>
+</ol>
+
+
+<h2 style="clear:both">Continuing activities on phone</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 20px 40px">
+  <img src="{@docRoot}design/media/wear/continue_phone.png">
+</div>
+
+<p>Developers should attempt to perform actions on the wearable device wherever possible. In cases where the phone must be used, a generic animation should be played once the action button has been tapped and the corresponding Android app will open on the phone.</p>
+
+
+<h2 style="clear:both">Actions on cards (such as media controls)</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 20px 40px">
+  <img src="{@docRoot}design/media/wear/action_on_card.png">
+</div>
+
+<p>Some cards may benefit from having tappable actions directly on a card. Some guidance on when to use this pattern versus using an action button:</p>
+
+<ul>
+<li>This pattern should be used when only one possible action could be reasonably expected. For example, tapping on an address with a car icon and ETA seems like it would very obviously launch directions. Conversely, if you see a contact's photo and name, it's not clear what tapping would do (call them? email them?), so the pattern shouldn't be used in this case.</li>
+<li>On-card actions should not require a text label to be understood.</li>
+<li>On-card actions should only result in something happening on the wearable (apart from web links to open them on the phone).</li>
+<li>Only one action per card: no menus on a single card.</li>
+</ul>
+
+<p>Good examples of using an action on card include: play / pause music; toggle light switch on and off; navigate to an address; call a phone number.</p>
+
+
+<h2 style="clear:both">Card stacks</h2>
+<img src="/wear/images/11_bundles_B.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="">
+<img src="/wear/images/11_bundles_A.png" height="200" width="169" style="float:right;margin:0 0 20px 40px" alt="">
+<p>Card stacks group related cards together and allow them to be progressively expanded vertically in the stream. A tap on a stack fans the cards out so that the top edge of each card can be seen. A subsequent tap on a fanned card reveals that card fully. Stacks of cards revert to a fully collapsed state once the user has swiped away from them.</p>
+
+
+
+<h2 style="clear:both">2D Picker</h2>
+
+<p>A 2D Picker component in your app can be invoked from the cue card or from an action button. It allows users to choose from a list of items, and optionally select an attribute of each item. For example, in response to a voice action to “buy tickets to a movie tonight,” you could show a 2D Picker with a vertical list of movies playing, with each movie having a horizontal list of showtimes.</p>
+
+<img src="{@docRoot}design/media/wear/2D_picker_action.png" width="500" alt="">
+
+<p>In some instances, further information may be required. In these cases, the most probable default values for these choices should be chosen on the user’s behalf with the option to edit before completing the action. This pattern is in keeping with Android Wear’s core design principle of minimizing interactions required.</p>
+
+
+<h2 style="clear:both">Voice commands</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 20px 40px">
+  <img src="{@docRoot}design/media/wear/voice_commands.png">
+</div>
+
+<p>It is possible for apps to take action in response to Android voice commands that invoke intents. For example, an app can register for the “Take a note” intent and capture the subsequent voice input for processing. In the case where multiple apps registered for the same intent, user preference will be captured once and saved. Users can edit their intent preferences in the Android Wear app on their handheld.</p>
+
+
+<h2 style="clear:both">Selection List</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 20px 40px">
+  <img src="{@docRoot}design/media/wear/selection_list.png">
+</div>
+
+<p>Choosing an item from a list is a common interaction. The Selection List pattern (available as the WearableListView component) creates a simple list optimized for ease of use on a small screen: the focused item snaps to the center of the screen, and a single tap selects. This widget is recommended as a common pattern for selecting items. It is used throughout the system UI, including in the list that can be accessed by swiping up on the cue card.</p>
+
+
+<p>Of course, it is possible for Android Wear apps to extend themselves beyond the familiarities of these patterns. For a deeper look at the options available, see the section on App Structure.</p>
diff --git a/docs/html/design/wear/principles.jd b/docs/html/design/wear/principles.jd
new file mode 100644
index 0000000..a214435
--- /dev/null
+++ b/docs/html/design/wear/principles.jd
@@ -0,0 +1,52 @@
+page.title=Design Principles for Wear
+@jd:body
+
+<style>
+p.try {
+  background:#e4e4e4;
+  padding:10px;
+}
+</style>
+
+<p>These design principles provide some simple heuristics about how you should plan and assess your
+Android Wear app design.</p>
+
+
+<h2>Focus on not stopping the user and all else will follow</h2>
+<p>A watch is a perfect form factor for a device that you can use while doing something else, such as cooking, eating, walking, running, or even having a conversation. If using your wearable app causes the user to stop whatever they’re doing, it’s a good occasion to consider how to improve it using the principles in this section.</p>
+
+<p class="try"><strong>Try this:</strong> Time a typical use of your Wear app. If using it takes more than 5 seconds, you should think about making your app more focused. Also try using your app while you’re having a conversation, and see how it affects your train of thought and eye contact.</p>
+
+
+<h2>Design for big gestures</h2>
+
+<p>When you swipe through photos on your phone you’re using a large area of the display, and you don’t have to be precise at all. That’s the best kind of interaction for a wearable device. Your users are going to use your app in all sorts of situations, the least frequent one might actually be sitting down at their desk.</p>
+
+<p class="try"><strong>Try this:</strong> Use your app in various everyday situations, such as walking, eating, talking to people, or ordering coffee. If you have to slow down while walking or stop the conversation to be precise, you should consider how your gestures could be bigger.</p>
+
+<h2>Think about stream cards first</h2>
+<p>The best experience on a wearable device is when the right content is there just when the user needs it. You can figure out when to show your cards with sensors, or events happening in the cloud. For the cases where it’s impossible to know when the user needs your app, you can rely on a voice action or touch.</p>
+
+<p class="try"><strong>Try this:</strong> Make a list of all the situations a user would find your app useful. What do they have in common? Same location? Time of day? Certain physical activities? You will most likely come up with several different situations - that’s a good sign, because it means that you can specialize your cards to those situations. Remember that the user always has the option of completely muting your stream cards if they feel they aren’t relevant enough.</p>
+
+
+<p>[image] </p>
+<p class="img-caption">An app that offers to check in users could appear in the stream suggesting the most likely place nearby, after a certain amount of time.</p>
+
+
+<h2>Do one thing, really fast</h2>
+<p>While users will engage with your app for only a few seconds at time, they'll use it many times throughout the day. A well-designed stream card carries one bit of information and potentially offers a few action buttons when the user swipes over.</p>
+
+<p class="try"><strong>Try this:</strong> How many bits of information is there in your design? Is everything absolutely necessary, or could you split it up into separate cards? If you’re designing a card, don’t forget that you can use multiple pages.</p>
+
+
+<h2>Design for the corner of the eye</h2>
+<p>The longer the user is looking at your app, the more you are pulling them out of the real world. Thinking about how to design your app for glanceability can vastly help the user get full value from your app and quickly go back to what they were doing.</p>
+
+<p class="try"><strong>Try this:</strong> To view your app with your peripheral vision, try focusing on your knuckles while your watch is displaying the app. Do you get a sense of what it is trying to do? Is it distinguishable from other apps? Does the background image help conveying the message? Does it use photos or a distinct shape and color?</p>
+
+<h2>
+Don’t be a constant shoulder tapper</h2>
+<p>A watch constantly touches the user’s skin. Being this intimate, you want to buzz the watch fewer times than you’re used to on the phone.</p>
+
+<p class="try"><strong>Try this:</strong> Next time you’re in a conversation, imagine someone tapping you your shoulder, interrupting you with the information you want your app to deliver. If the information delivered did not justify suspending a conversation, you should not make the notification interruptive.</p>
\ No newline at end of file
diff --git a/docs/html/design/wear/structure.jd b/docs/html/design/wear/structure.jd
new file mode 100644
index 0000000..caeb119
--- /dev/null
+++ b/docs/html/design/wear/structure.jd
@@ -0,0 +1,116 @@
+page.title=App Structure for Wear
+@jd:body
+
+
+<p>As outlined in the <a href="{@docRoot}design/wear/creative-vision.html">Creative Vision</a>,
+Android Wear apps do not adhere to the traditional mobile app model of touching an icon to launch into a self-contained experience. Rather, it is useful to think about the different spaces in the Android Wear UI and how your app might present itself across these spaces. For example, a typical app might begin by showing a notification card in the stream at a contextually relevant moment, then jump into a custom full screen UI for a micro-interaction, or maybe open the cue card to capture voice input that is then relayed back to the in-stream card.</p>
+
+<p>It’s important to make some fundamental decisions about how your users will interact with your app. There are a number of ways that functionality can manifest itself in Android Wear, and it is important to choose the places that provide maximum value and ease of use.</p>
+
+<p>For example, application functionality might show up in the following ways:</p>
+
+<ul>
+  <li>As a <strong>card in the main context stream</strong>:
+  <ul>
+    <li><strong>Bridged notifications</strong> are pushed to the wearable from the connected handheld (a phone or tablet) using the standard Android notifications framework. In general, bridged notifications mirror what’s happening on the handheld and use one of a predefined layout templates. Example: new message notification.
+    <li><strong>Contextual notifications</strong> are like smart notifications. They are generated locally on the wearable and appear at contextually relevant moments specificed by the app developer. Contextual notifications allow more freedom of control, allowing for custom layouts and dynamic updating of card contents. Example: live updating exercise stats.
+  </ul>
+  </li>
+  <li>As a <strong>full screen UI</strong> that temporarily overlays on top of the context stream:
+  <ul>
+    <li>The <strong>2D Picker</strong> is a simple design pattern (available in the SDK as a prebuilt component) aimed at asking the user to select from a set of items. This is a common interaction and a familiar pattern, so use of the familiar 2D Picker pattern is encouraged wherever possible. Example: choose from a set of artists and albums to play.
+    <li><strong>Custom layouts</strong> are also possible where apps need to extend beyond the basic card/stream metaphor. These apps should be distinctly separate from the core user experience in both appearance and interaction.</li>
+  </ul>
+  </li>
+</ul>
+
+
+<p>Apps can also open the cue card to capture voice input.</p>
+
+<p>Note that the different approaches above are listed in order of complexity. When designing your interactions, try to achieve them with the simplest approach possible. If your needs are more involved, move to the next level of complexity.</p>
+
+<p>Many applications will consist of a combination of these views, possibly with connections between them. For example, a contextual card may have an action that launches a more immersive experience. Inversely, an immersive experience may result in a card being added to the stream.</p>
+
+<p>Think of these different components as building blocks that can be snapped together into a single user flow. Avoid single monolithic full screen UIs that need to be launched and quit. Place simple notifications and ongoing information in the stream, and jump in and out of simple full screen activities to complete quick tasks before returning to the stream.</p>
+
+<p>In this section we will look at these different approaches and how combine them to create the best experience for your users.</p>
+
+
+<h2 id="Bridged">Bridged Notifications</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 -22px 60px 40px">
+  <img src="{@docRoot}design/media/wear/bridgednotifications.jpg">
+</div>
+
+<p>Bridged notifications are the simplest way of having content appear on an Android wearable. Since cards in the Android Wear stream are synced from the notifications on your connected handheld, any notifications created there automatically appear on Android Wear. Where appropriate, make sure to use the new APIs in the support library that enhance your app's notifications with features such as voice replies and notification pages.</p>
+
+
+
+<h2 id="Contextual">Contextual Notifications</h2>
+
+<p>Displaying information on contextual cards is at the core of the Android Wear user experience. Cards with focused trigger criteria appear at just the right time, delighting and assisting the user with timely, useful content. Refer to the Design principles section for details on how to think about triggering cards, and the UI overview section for a breakdown of the familiar system UI components that make up an entry in the context stream.</p>
+
+<p>An important aspect of creating contextual notifications is defining trigger conditions: what is the specific scenario in which your notification should appear? Think about using all sensor information available to you -- time, location, movement, identity, user habits and patterns, interaction with nearby devices, and more -- and describe the specific combination of sensor readings that should result in your app presenting itself.</p>
+
+
+<img src="{@docRoot}design/media/wear/contextualnotification.png" width="500" alt="" />
+
+
+<p>For example, imagine you were building a running app. If the user is standing at the beginning of their regular running trail, at the time that they often go for a run, and we detect a running activity... they are probably going for a run! This would be a great time to present a contextual card that offers to track their run.</p>
+
+<p>Putting effort into getting your contextual triggering just right is one of the most impactful things you can do to create a delightful experience for your users.</p>
+
+<p>You may use the standard Android notifications framework to create cards using a range of provided templates, or draw your own ActivityView inside cards for a custom card layout. Make sure to refer to the Style section to make sure your custom ActivityView layouts are sympathetic to the overall design of cards in the stream. Use ActivityViews to create custom card layouts that are stylistically consistent with neighboring cards in the stream; do not invent entirely new or conflicting UI patterns inside cards.</p>
+
+<p>Contextual cards are ideal for situations where it may be useful to push information to the user, when the information may be useful on an ongoing basis or referred back to, or when context strongly indicates that the information is useful. Refer to the section on Respecting Users Attention in the Design Principles section for details on targeted triggering. Triggering too often or in unsuitable contexts will result in users being annoyed by your app.</p>
+
+
+
+<h2 id="Picker">2D Picker</h2>
+
+<p>The 2D Picker design pattern (available as the GridViewPager component) is useful for showing a range of options or asking a user to make a quick selection. Google search results on Android Wear are a great example of the GridViewPager pattern in action.</p>
+
+<p>A 2D Picker is called up as an overlay on the main UI by tapping a card or button, or through a voice action. It preserves the same look and feel as the main context stream, giving users a familiar and predictable set of interaction patterns to rely on.</p>
+
+<p>On Android Wear, the basic hierarchy is vertical-then-horizontal, never horizontal-then-vertical, with a recommended vertical limit of five cards. Each vertical slot may consist of one card, as in the Google results case, or multiple cards which may be swiped horizontally.</p>
+
+<img src="{@docRoot}design/media/wear/1D_picker.png" alt="" width="499px" />
+<p class="img-caption">This pattern can be used to present a single vertical list, or a “1D Picker”</p>
+
+<img src="{@docRoot}design/media/wear/2D_picker.png" alt="" width:760px" />
+<p class="img-caption">It can also be used as a 2D matrix of options, as a way of presenting categorized options.</p>
+
+<p>This flexibility means that developers can choose to present a one or two-dimensional set of options. For example, a music app could use a vertical list to present a list of albums by a given artist (one dimension of options), and it could additionally allow each album slot to be horizontally swipeable to also choose a song from each album (a second dimension of options).</p>
+
+<p>Do not add buttons or pages to horizontal 2D Picker rows; rows should only be used to present a list or grid of similar options in this context. Provide a clear call to action on the card using the Action cards pattern detailed in the <a href="{@docRoot}design/wear/patterns.html">UI Patterns</a> guide.</p>
+
+<p>2D Picker should be automatically dismissed when a selection is made. It may also be exited by swiping back down on the first card, or by swiping left to right on a leftmost card.</p>
+
+<p>The simplicity of individual cards within a 2D Picker is a feature. Remember that in many cases the user may be on the go or attempting to complete a task as quickly as possible. As such, micro-interactions and familiar input mechanism are paramount, and using the already-familiar pattern of vertically-then-horizontally oriented cards gives users exactly what they want with as little fuss as possible. Strive to minimize the number of results or options that you present. Show the most popular or repeatedly-used options at the top of the list of cards to avoid scrolling. Learn the user's preferences and use context detection to put the most likely option for any given situation at the top. In general, optimize for fast task completion over excessive customization.</p>
+
+
+
+<h2 id="Custom">Custom Layouts</h2>
+
+<p>Some interactions may require a broader range of input mechanisms than is possible within the limitations of a card-based UI. For example, an app that allows for location selection may require the user to swipe in many directions before tapping to drop a pin. In cases like this, it is recommended to momentarily launch out of the context stream UI and present an immersive, full screen app with a custom layout.</p>
+
+<p>Custom full screen apps provides the benefit of flexibility: you can launch your own Android activity that takes up the entire screen, and capture all touch events, making a wide range of UIs possible.</p>
+
+<img src="{@docRoot}design/media/wear/customlayout.jpg" alt="" width="760px" />
+
+
+<p>However, be cautious of making this the default way of accessing your app’s functionality. Users will thank you for presenting your content in the familiar, simple environment of the context stream if it is possible to do so. Only enter full screen mode when the interactions required are not possible using the card UI. Full screen is a modal state to be entered for the purpose of achieving a specific task, and in most cases should be easily and quickly exited. You should use full screen apps to achieve a single, quick task within a broader user flow that hinges off the Context Stream. A great full screen experience will present itself quickly, ask for some user input, and then self-quit back to the stream.</p>
+
+<p>To avoid confusion, avoid using the specific styles and idioms of the context stream when designing immersive experiences. If you find yourself replicating the structure of the card layout, your should probably be using a 2D Picker. Make your immersive experience visually distinct. However, still adhere to the <a href="{@docRoot}design/wear/principles.html">Design Principles</a>, which apply universally to Android Wear interfaces.</p>
+
+<p>Because Android wearables do not feature a home or back button, exiting the application at the appropriate time is the responsibility of the app developer. Exiting always leads back to the context stream. Where possible, exit automatically or present the option to exit at logical break points using acknowledge/cancel buttons. For example:</p>
+
+<ol>
+<li>A map view that allows the user to slide a map to drop a pin on a location should automatically exit when the pin has been placed.</li>
+<li>A short game should automatically exit back to the stream at the end of each game.</li>
+<li>A drawing app should display the option to exit after 5 seconds of inactivity.</li>
+</ol>
+
+<p>Even with logical exit points like these, some cases may exist where the user may want to immediately initiate an exit. This may be particularly common in apps of longer duration. In all cases, the developer should <strong>present the option to quit the app on long press</strong> using DismissOverlayView. Your design should long press for the sole purpose of prompting to quit.</p>
+
+<p>Seamlessly and fluidly moving between the context stream and immersive mode makes your app feel like an integrated part of the Android Wear experience.</p>
\ No newline at end of file
diff --git a/docs/html/design/wear/style.jd b/docs/html/design/wear/style.jd
new file mode 100644
index 0000000..ed39bd6
--- /dev/null
+++ b/docs/html/design/wear/style.jd
@@ -0,0 +1,102 @@
+page.title=Style for Wear
+@jd:body
+
+
+<p>Here are a number of design considerations to bear in mind that are particular to Android Wear.</p>
+
+<h2 id="ScreenSize">Screen Size</h2>
+
+<img src="{@docRoot}design/media/wear/circle_message2.png" height="200"
+   style="float:right;margin:0 0 20px 60px">
+
+<img src="{@docRoot}design/media/wear/fitness.png" height="200"
+  style="float:right;margin:0 0 20px 60px">
+
+<p>Be mindful of different device sizes and shapes. Wearable devices are a form of fashion and expression for their owners, and so Android Wear supports  a variety of forms. Most of the complexities of supporting these different devices is taken care of at a system level, but bear in mind different screen types when designing custom full screen apps.</p>
+
+<p>Use the Android Wear emulator to test both square and round devices, and note that <code>WatchViewStub</code> is available to activities to detect whether a square or round device is being used.</p>
+
+
+
+
+<h2 id="Assets" style="clear:both">Specific Assets Required</h2>
+
+<img src="{@docRoot}design/media/wear/assets_specifics.png" width="300"
+  style="float:left;margin:0 60px 20px 0">
+
+<p>A core set of standard assets may need to be provided depending on your card design: app icon, background image or images, action icons, actions confirmation animation. Of course, your specific design may necessitate other assets. Background image should be provided in landscape format at least 600px width for notifications that include pages of cards, since the system automatically adds a parallaxing effect.</p>
+
+
+
+<h2 id="PeekCard" style="clear:both">Peek Card Readability</h2>
+
+<img src="{@docRoot}design/media/wear/peek_card.png" width="300"
+  style="float:left;margin:0 60px 20px 0">
+
+<p>Test your card layout to ensure that useful information is conveyed in the peek state on the Home screen. The main message of the card should be readable in the peek state, particularly for contextual cards. Content that requires an interaction to be read, for example a long message, should be cropped appropriately to provide an affordance to the user to swipe the card to read more.</p>
+
+
+
+<h2 id="InfoDensity" style="clear:both">Low Information Density</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 40px 60px">
+  <img src="{@docRoot}design/media/wear/low_info_card.png">
+</div>
+
+<p>Cards should be designed to be glanceable in a split second, just like reading the time on a traditional watch. In most cases a pairing of an icon and value, or a title and short caption should be enough to convey a meaningful message. Note that the background photo should also be used to convey information; backgrounds that change to reflect and support the primary message in the card work great. For example, in the case illustrated above a suitable background image is chosen to reflect severity of the current traffic conditions. This is not just a nice piece of attention to detail; the background actually reinforces the message and makes the content more glanceable.</p>
+
+
+<h2 id="Chunks" style="clear:both">Separate Information into Chunks</h2>
+
+<img src="{@docRoot}design/media/wear/separate_info_cards.jpg" width="400"
+  style="float:left;margin:0 60px 20px 0">
+
+<p>In cases where additional information is absolutely necessary, don’t crowd out a card layout to the point where glanceability is affected. Instead, add an additional page (or multiple pages, if needed) to the right of the main card in the stream to which the user can swipe for more information. See also “Continuing activities on phone”, below.</p>
+
+
+<h2 id="KeepMinimum" style="clear:both">Keep Notifications to a Minimum</h2>
+
+<p>Don’t abuse the user’s attention. Active notifications (that is, those that cause the device to vibrate) should only be used in cases that are both timely and involve a contact, for example receiving a message from a friend. Non-urgent notifications should be silently added to the Context Stream. See also the general Android Notifications Guidelines.</p>
+
+
+<h2 id="Typography" style="clear:both">Use Clear, Bold Typography</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 60px 40px">
+  <img src="{@docRoot}design/media/wear/clear_bold_type.jpg">
+</div>
+
+<p>The system font is Roboto Condensed, with Regular and Light variants. Text should adhere to the size and color recommendations (see the UI Toolkit in the Downloads section). In general, text should be displayed as large as possible. Your goal should be to convey maximum information with minimum fuss.</p>
+
+
+<h2 id="Branding" >Use Consistent Branding and Color</h2>
+
+<p>The app icon is used to identify and brand your application. The icon is optional but when present always appears in the same location, overhanging the top edge of the card at the right. Note that app icons or branding should not be displayed in the background photo, which is reserved to display an image relevant to the information on the card.</p>
+
+
+<h2 id="Copywrite" style="clear:both">Copywrite Sparingly</h2>
+
+<div class="framed-wear-square-small" style="float:right;margin:0 0 60px 40px">
+  <img src="{@docRoot}design/media/wear/copywrite.png">
+</div>
+
+<p>Omit needless text. Design for glanceability, not reading. Use words and phrases, not sentences. Use icons paired with values instead of text wherever possible. Text strings should be as concise as possible, and long pieces of text will be truncated to fit on a single card.</p>
+
+
+<h2 id="BeDiscreet" >Be Discreet if Necessary</h2>
+
+<p>Wearables are personal devices by nature, but they are not completely private. If your notification serves content that may be particularly sensitive or embarrassing (such as notifications from a dating app or a medical status report), consider not displaying all of the information in a peek card. A notification could place the sensitive information on a second page that must be swiped to, or an application could show different amounts of detail in peek and focused card positions.</p>
+
+
+<h2 id="ConfirmAnim" style="clear:both">Confirmation Animations</h2>
+
+<div class="framed-wear-square-small" style="float:left;margin:0 40px 40px 0 ">
+  <img src="{@docRoot}design/media/wear/confirmation.png">
+</div>
+
+<p>If your app allows the user to perform an action, it is necessary to provide positive feedback. Show a generic confirmation animation or create your own. A confirmation animation is an opportunity to express your app’s character and insert a moment of delight for your user. Keep animations short (less than 1000ms) and simple. Animating the confirmation icon is an effective way of transitions the user to a new state after completing an action.</p>
+
+
+
+
+
+
diff --git a/docs/html/distribute/essentials/essentials_toc.cs b/docs/html/distribute/essentials/essentials_toc.cs
index 7084fdd..d0a1114 100644
--- a/docs/html/distribute/essentials/essentials_toc.cs
+++ b/docs/html/distribute/essentials/essentials_toc.cs
@@ -11,12 +11,6 @@
     </div>
   </li>
   <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/essentials/gpfe-guidelines.html">
-          <span class="en">Education Guidelines</span>
-        </a>
-    </div>
-  </li>
-  <li class="nav-section">
     <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/essentials/optimizing-your-app.html">
           <span class="en">Optimize Your App</span>
         </a>
diff --git a/docs/html/distribute/essentials/gpfe-guidelines.jd b/docs/html/distribute/essentials/gpfe-guidelines.jd
deleted file mode 100644
index 734bddc..0000000
--- a/docs/html/distribute/essentials/gpfe-guidelines.jd
+++ /dev/null
@@ -1,509 +0,0 @@
-page.title=Education Guidelines
-page.metaDescription=These guidelines and requirements help you develop great apps for students, which offer compelling content and an intuitive user experience on Android tablets.
-page.image=/distribute/images/edu-guidelines.jpg
-Xnonavpage=true
-
-@jd:body
-
-
-<div id="qv-wrapper"><div id="qv">
-<h2>Guidelines</h2>
-<ol>
-<li><a href="#basic-reqts">Basic Requirements</a></li>
-<li><a href="#monetizing-ads">Monetizing and Ads</a></li>
-<li><a href="#e-value">Educational Value</a></li>
-<li><a href="#quality">App Quality</a></li>
-<li><a href="#related-resources">Related Resources</a></li>
-</ol>
-
-<h2>
-  Testing
-</h2>
-
-<ol>
-  <li>
-    <a href="#test-environment">Setting Up a Test Environment</a>
-  </li>
-</ol>
-
-</div></div>
-
-<div style="margin:0 0 1em 0;">
-  <img src="{@docRoot}distribute/images/edu-guidelines.jpg" style=
-  "width:274px;">
-</div>
-
-<p>
-  These guidelines and requirements help you develop great apps for students,
-  which offer compelling content and an intuitive user experience on Android
-  tablets.
-</p>
-
-<p>
-  You’ll also need to ensure that your apps comply with the terms of the
-  <a href=
-  "https://play.google.com/about/developer-distribution-agreement-addendum.html">
-  Google Play for Education Addendum</a>, <a href=
-  "http://play.google.com/about/developer-content-policy.html">Google Play
-  Developer Program Policies</a>, and <a href=
-  "http://play.google.com/about/developer-distribution-agreement.html">Developer
-  Distribution Agreement</a>.
-</p>
-
-<div class="headerLine">
-  <h2 id="basic-reqts">
-    Basic Requirements
-  </h2>
-
-
-</div>
-
-<p>
-  To participate, your apps must be designed for the K-12 market. The basic
-  requirements that your apps must meet are:
-</p>
-
-<ul>
-  <li>
-    <p>
-      Apps and the ads they contain must not collect personally identifiable
-      information, other than user credentials or data required to operate and
-      improve the app.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      Apps must not use student data for purposes unrelated to its educational
-      function.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      Apps must have a content rating of "Everyone" or "Low Maturity" (apps
-      with a "Medium Maturity" rating are allowed, if they have that rating
-      solely because they allow communication between students).
-    </p>
-  </li>
-
-  <li>
-    <p>
-      App content, including ads displayed by the app, must be consistent with
-      the app's maturity rating. The app must not display any "offensive"
-      content, as described in the <a href=
-      "http://play.google.com/about/developer-content-policy.html">Google Play
-      Developer Program Policies</a> and <a href=
-      "https://support.google.com/googleplay/android-developer/answer/188189">content-rating
-      guidelines</a>.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      Apps must comply with the Children’s Online Privacy Protection Act and
-      all other applicable laws and regulations.
-    </p>
-  </li>
-</ul>
-
-<div class="headerLine">
-  <h2 id="monetizing-ads">
-    Monetizing and Ads
-  </h2>
-
-
-</div>
-
-<p>
-  In-app purchase is currently not supported with Google Play for Education, so
-  a student device will block any transactions. To avoid confusion, be sure to
-  remove any in-app purchase buttons and related UI elements from your apps.
-  We’re investigating additional purchase mechanisms to enable more flexible
-  pricing models for developers and schools.
-</p>
-
-<p>
-  If your apps are priced In Google Play for Education, you must allow Google
-  Play to offer teachers limited free trials before purchase (you provide this
-  through business terms only, no development work is needed.)
-</p>
-
-<p>
-  You can only choose not to remove in-app purchasing from your apps where all
-  content and services are sold through Google Play for Education using In-app
-  Billing. If you choose not to remove In-app Billing features, ensure that:
-</p>
-
-<ul>
-  <li>
-    <p>
-      Users can access your apps’ core functionality for a classroom setting
-      without an in-app purchase.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      In-app purchases are clearly identifiable in your UI.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      You declare the use of in-app purchases at <a href=
-      "{@docRoot}distribute/googleplay/edu/start.html#publish">opt-in</a>.
-    </p>
-  </li>
-</ul>
-
-<p>
-  For each app that you publish, you can set a single price that applies to
-  both Google Play and Google Play for Education. You can’t set a different
-  price for a given app (based on a single package name) in Google Play for
-  Education.
-</p>
-
-<p>
-  If your apps display ads, you should disable the display of ads if possible,
-  or ensure that:
-</p>
-
-<ul>
-  <li>Ads are not distracting for students or teachers (this includes
-  Flash-based ads, video ads, and ads that flash or move)
-  </li>
-
-  <li>Interstitial ads are not served in the app
-  </li>
-
-  <li>Ad walls do not appear in the app UI
-  </li>
-
-  <li>Ads do not occupy a significant portion of the screen
-  </li>
-
-  <li>Ads content does not exceed the maturity rating of the app.
-  </li>
-
-  <li>
-    <p>
-      You declare the use of ads at <a href=
-      "{@docRoot}distribute/googleplay/edu/start.html#publish">opt-in</a>.
-    </p>
-  </li>
-</ul>
-
-<div class="headerLine">
-  <h2 id="e-value">
-    Educational Value
-  </h2>
-
-
-</div>
-
-<div class="figure">
-  <img src="{@docRoot}images/gp-e-value.png" class="border-img">
-</div>
-
-<p>
-  Apps submitted to Google Play for Education will be evaluated by a
-  third-party educator network, which will review them based on alignment with
-  <a href="http://www.corestandards.org/">Common Core Standards</a> and other
-  educational considerations. This will help make your content more
-  discoverable for teachers and administrators as they browse by grade level,
-  subject, core curriculum, and other parameters.
-</p>
-
-<p>
-  Apps with highest educational value will have these characteristics:
-</p>
-
-<ul>
-  <li>
-    <p>
-      Designed for use in K-12 classrooms.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      Aligned with a common core standard or support common-core learning.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      Simple, easy to use, and intuitive for the grade levels the apps are
-      targeting. Apps are relatively easy to navigate without teacher guidance.
-      Not distracting or overwhelming to students.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      Enjoyable and interactive. Apps are engaging to students and lets them
-      control their experience.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      Versatile. Apps have features that make them useful for more than one
-      classroom function or lesson throughout the school year.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      Supports the "4Cs":
-    </p>
-
-    <ul>
-      <li>
-        <p>
-          <em>Creativity</em> &mdash; Allows students to create in order to
-          express understanding of the learning objectives, and try new
-          approaches, innovation, and invention to get things done.
-        </p>
-      </li>
-
-      <li>
-        <p>
-          <em>Critical thinking</em> &mdash; Allows students to look at
-          problems in a new way, linking learning across subjects and
-          disciplines.
-        </p>
-      </li>
-
-      <li>
-        <p>
-          <em>Collaboration</em> &mdash; Allows students and (if appropriate)
-          educators to work together to reach a goal.
-        </p>
-      </li>
-
-      <li>
-        <p>
-          <em>Communication</em> &mdash; Allows students to comprehend,
-          critique and share thoughts, questions, ideas, and solutions.
-        </p>
-      </li>
-    </ul>
-  </li>
-</ul>
-
-<p>
-  As you design and develop your apps, make sure they offer high educational
-  value by addressing as many of these characteristics as possible.
-</p>
-
-<div class="headerLine">
-  <h2 id="quality">
-    App Quality
-  </h2>
-
-
-</div>
-
-<div class="figure">
-  <img src="{@docRoot}images/gp-edu-quality.png">
-</div>
-
-<p>
-  Your apps should be designed to perform well and look great on Android
-  tablets, and they should offer the best user experience possible.
-</p>
-
-<p>
-  High quality apps are engaging, intuitive, and offer compelling content.
-  Google Play for Education will highlight high-quality apps for easy discovery
-  in the store. Here are some recommendations for making your app easy for
-  students and teachers to enjoy:
-</p>
-
-<ul>
-  <li>
-    <p>
-      Meet the Core Quality Guidelines:
-    </p>
-
-    <ul>
-      <li>
-        <p>
-          Follow <a href="{@docRoot}design/index.html">Android Design
-          Guidelines</a>. Pay special attention to the sections on <a href=
-          "{@docRoot}design/patterns/actionbar.html">Action Bar</a>, <a href=
-          "{@docRoot}design/patterns/navigation.html">Navigation</a>, and
-          <a href="{@docRoot}design/patterns/pure-android.html">Pure
-          Android</a>.
-        </p>
-      </li>
-
-      <li>
-        <p>
-          Test your apps against the <a href=
-          "{@docRoot}distribute/essentials/quality/core.html">Core Quality
-          Guidelines</a>.
-        </p>
-      </li>
-    </ul>
-  </li>
-
-  <li>
-    <p>
-      Meet the Tablet App Quality guidelines:
-    </p>
-
-    <ul>
-      <li>
-        <p>
-          Follow our best practices for tablet app development.
-        </p>
-      </li>
-
-      <li>
-        <p>
-          Review the <a href=
-          "{@docRoot}distribute/essentials/quality/tablets.html">Tablet App
-          Quality</a> guidelines and <a href=
-          "http://android-developers.blogspot.com/2012/11/designing-for-tablets-were-here-to-help.html">
-          blog post on designing for tablets.</a>
-        </p>
-
-        <ul>
-          <li>Check your Optimization Tips in the <a href=
-          "https://play.google.com/apps/publish/">Developer Console</a> (if
-          you've already uploaded your apps.)
-          </li>
-        </ul>
-      </li>
-
-      <li>
-        <p>
-          Strive for simplicity and highest usability for students:
-        </p>
-
-        <ul>
-          <li>
-            <p>
-              Design your app so that teachers and students can use all the
-              capabilities of your app without having to sign-in to multiple
-              accounts and remember multiple passwords.
-            </p>
-          </li>
-
-          <li>
-            <p>
-              Every student or teacher using a Google Play for Education tablet
-              will already be signed in with a Google account on the device.
-              You can take advantage of that to provide a simple, seamless
-              sign-in experience in your app. A recommended approach is to use
-              <a href="{@docRoot}google/play-services/auth.html">Google OAuth 2
-              authorization</a> through Google Play Services.
-            </p>
-          </li>
-        </ul>
-      </li>
-    </ul>
-  </li>
-</ul>
-
-<div class="headerLine">
-  <h2 id="test-environment">
-    Test Environment
-  </h2>
-
-
-</div>
-
-<p>
-  To test your app and assess it against the guidelines in this document, it's
-  recommended that you <a href=
-  "{@docRoot}distribute/essentials/quality/tablets.html#test-environment">set
-  up a test environment</a> that replicates the actual environment in which
-  students and teachers will run your app.
-</p>
-
-<h3>
-  Test conditions
-</h3>
-
-<p>
-  Make sure to test your apps under conditions that simulate those of schools.
-  For example, Google Play for Education lets administrators <a href=
-  "https://support.google.com/a/answer/182442?hl=en">control or disable certain
-  capabilities</a> for students, so it's good to test your app with those
-  capabilities disabled. Below are some conditions to test your apps for, to
-  ensure best results in the Google Play for Education environment:
-</p>
-
-<ul>
-  <li>
-    <p>
-      <em>Android version</em> &mdash; Test the apps on devices running Android
-      4.2. Google Play for Education devices will be running Android 4.2 or
-      higher (API level 17+).
-    </p>
-  </li>
-
-  <li>
-    <p>
-      <em>Proxy server</em> &mdash; Test the apps in a network environment that
-      uses proxies. Many schools use proxies.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      <em>No location services</em> &mdash; Test the apps to make sure they
-      work properly with location services disabled. Many schools will disable
-      location services for student devices.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      <em>No In-app Billing</em> &mdash; Test the apps to make sure they work
-      properly without access to In-app Billing. In-app purchases are blocked
-      on Google Play for Education devices.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      <em>No Bluetooth</em> &mdash; Test the apps to make sure they work
-      properly when Bluetooth is disabled. Many schools will disable Bluetooth
-      on student devices.
-    </p>
-  </li>
-
-  <li>
-    <p>
-      <em>No access to network</em> &mdash; Test the app to make sure it works
-      properly when the device cannot connect to the internet.
-    </p>
-  </li>
-</ul>
-
-<div class="headerLine">
-<h2>Related Resources</h2>
-</div>
-
-<div class="dynamic-grid">
-<h3>FOR DEVELOPERS</h3>
-
-<div class="resource-widget resource-flow-layout col-13"
-    data-query="collection:distribute/essentials/eduessentials/developers"
-    data-sortOrder="-timestamp"
-    data-cardSizes="6x3,6x3,6x3"
-    data-maxResults="6"></div>
-
-<h3>FOR TEACHERS AND EDUCATORS</h3>
-
-<div class="resource-widget resource-flow-layout col-13"
-    data-query="collection:distribute/essentials/eduessentials/educators"
-    data-sortOrder="-timestamp"
-    data-cardSizes="6x3,6x3,6x3"
-    data-maxResults="3"></div>
-</div>
\ No newline at end of file
diff --git a/docs/html/distribute/googleplay/edu/about.jd b/docs/html/distribute/googleplay/edu/about.jd
index e73356e..60d9402 100644
--- a/docs/html/distribute/googleplay/edu/about.jd
+++ b/docs/html/distribute/googleplay/edu/about.jd
@@ -9,7 +9,7 @@
 
 <p>
   Google Play for Education is an extension of Google Play designed for
-  schools. Here educators can discover apps approved by teachers for teachers,
+  schools. Here educators can discover Android and Chrome apps approved by teachers for teachers,
   as well as educational videos and a collection of classic books for their
   classroom.
 </p>
@@ -22,7 +22,7 @@
 </p>
 
 
-<div class="resource-widget resource-flow-layout col-13"
+<div class="resource-widget resource-flow-layout col-13" style="height:323px"
   data-query="collection:distribute/googleplay/gpfe/highlight"
   data-sortOrder="-timestamp"
   data-cardSizes="18x6,"
diff --git a/docs/html/distribute/googleplay/edu/faq.jd b/docs/html/distribute/googleplay/edu/faq.jd
deleted file mode 100644
index 36e2064..0000000
--- a/docs/html/distribute/googleplay/edu/faq.jd
+++ /dev/null
@@ -1,433 +0,0 @@
-page.title=Education FAQ
-meta.tags="gpfe, edu"
-page.metaDescription=Answers to frequent questions about Google Play for Education.
-page.image=/distribute/images/gpfe-faq.jpg
-
-@jd:body
-
-<div id="qv-wrapper">
-  <div id="qv">
-  <h2>
-    Topics
-  </h2>
-
-  <ol>
-    <li>
-    <a href="#business-model-and-monetization">Business Model and
-    Monetization</a>
-    </li>
-
-    <li>
-    <a href="#free-trials">Free Trials</a>
-    </li>
-
-    <li>
-    <a href="#discovery">Discovery</a>
-    </li>
-
-    <li>
-    <a href="#app-review-process">App Review Process</a>
-    </li>
-
-    <li>
-    <a href="#app-features">App Features</a>
-    </li>
-
-    <li>
-    <a href="#marketing-and-roi">Marketing and ROI</a>
-    </li>
-
-    <li>
-    <a href="#devices">Devices</a>
-    </li>
-
-    <li>
-    <a href="#accounts">Accounts</a>
-    </li>
-
-    <li>
-    <a href="#related-resources">Related Resources</a>
-    </li>
-  </ol>
-  </div>
-</div>
-
-<p>
-  This page provides answers to common questions that you might have about
-  Google Play for Education.
-</p>
-
-<div class="headerLine">
-  <h2 id="business-model-and-monetization">
-  Business Model and Monetization
-  </h2>
-
-
-</div>
-
-<p>
-  <strong>What is Google Play for Education?</strong>
-</p>
-
-<p>
-  Google Play for Education is a new online destination designed for schools.
-  Teachers can discover educational apps, books, and videos to meet the needs
-  of a single student, a classroom, or a whole district. Educators can browse
-  apps by grade, subject, keyword, or standard including Common Core State
-  Standards. Purchasing is done using a PO with no credit card required. Apps
-  are distributed to tablets instantly through the cloud.
-</p>
-
-<p>
-  <strong>Is Google Play for Education primarily for students or
-  educators?</strong>
-</p>
-
-<p>
-  The store on Google Play for Education is for educators, but its content is
-  for both educators and students. Teachers and administrators have the ability
-  to make purchases and control who within their school has access to the
-  purchase flows.
-</p>
-
-<div class="figure">
-  <img src="{@docRoot}distribute/images/gpfe-faq.jpg" style=
-  "width:480px;margin:1em 0em 1.5em 1.5em;">
-</div>
-
-<p>
-  <strong>Will Google Play for Education support subscription
-  purchases?</strong>
-</p>
-
-<p>
-  Currently, Google Play for Education supports one-time purchases. We’re
-  investigating additional purchase mechanisms to enable more flexible pricing
-  models for developers and schools.
-</p>
-
-<p>
-  <strong>Why is it recommended that in-app purchase features are
-  removed?</strong>
-</p>
-
-<p>
-  In-app Billing is currently not supported with Google Play for Education, and
-  a student device will block the Google Play transaction if a student attempts
-  to make an in-app purchase. To avoid confusing students, we recommend not
-  including any in-app purchase buttons and other UI in your apps. We’re
-  investigating additional purchase mechanisms to enable more flexible pricing
-  models for developers and schools.
-</p>
-
-<p>
-  <strong>Is Google Play for Education restricted so only its users can
-  purchase from the Google Play for Education? Or will anyone be able to
-  purchase from it?</strong>
-</p>
-
-<p>
-  Currently, only schools that are signed up for Google Play for Education can
-  make purchases on it.
-</p>
-
-<p>
-  <strong>Can I set different prices for my apps in Google Play for Education
-  and Google Play?</strong>
-</p>
-
-<p>
-  You set a single price for each app that applies to both Google Play and
-  Google Play for Education. You can’t set a different price for a given app
-  (based on a single package name) in Google Play for Education.
-</p>
-
-<div class="headerLine">
-  <h2 id="free-trials">
-  Free Trials
-  </h2>
-
-
-</div>
-
-<p>
-  <strong>Can I offer free trials through Google Play for Education?</strong>
-</p>
-
-<p>
-  Google Play for Education doesn't currently support free trials. If you want,
-  you can offer a free version of your app with limited functionality in Google
-  Play for Education, but that app would need to be separate from your paid app
-  and be reviewed separately for educational content.
-</p>
-
-<p>
-  <strong>Can I offer a free trial through Google Play's "In-app Subscriptions
-  with Free Trials" feature?</strong>
-</p>
-
-<p>
-  Google Play for Education doesn’t currently support In-app Billing or In-app
-  Subscriptions with free trials.
-</p>
-
-<div class="headerLine">
-  <h2 id="discovery">
-  Discovery
-  </h2>
-
-
-</div>
-
-<p>
-  <strong>What are the categories in Google Play for Education?</strong>
-</p>
-
-<p>
-  Google Play for Education includes categories for all grade levels from
-  Kindergarten to 12 and the following subjects: English Language Arts, World
-  Languages, Mathematics, Science, Social Science, Elective, Open Education
-  Resources (OER), and Tools.
-</p>
-
-<p>
-  <strong>I created an app specifically for Google Play for Education and don’t
-  want it to show up in Google Play. Is this possible?</strong>
-</p>
-
-<p>
-  Currently, it’s not possible to publish an app on Google Play for Education
-  and make it unavailable on Google Play.
-</p>
-
-<p>
-  <strong>If my app offers content for every level of education, how will it
-  fit the Common Core State Standard filters?</strong>
-</p>
-
-<p>
-  If your app applies to multiple levels of education, then the app will show
-  up in filtered results for multiple levels.
-</p>
-
-<div class="headerLine">
-  <h2 id="app-review-process">
-  App Review Process
-  </h2>
-
-
-</div>
-
-<p>
-  <strong>How are apps being reviewed? By whom and against what
-  criteria?</strong>
-</p>
-
-<p>
-  Apps are being reviewed by a third-party network of educators. These
-  educators assign the appropriate subject, grade, and Common Core State
-  Standards metadata, as well as evaluating whether the app meets the Google
-  Play for Education <a href=
-  "{@docRoot}distribute/essentials/gpfe-guidelines.html">criteria for classroom
-  use</a>.
-</p>
-
-<p>
-  <strong>How do I update my apps in Google Play for Education?</strong>
-</p>
-
-<p>
-  You can update your apps on Google Play for Education in the same manner you
-  do on Google Play. App updates will not be reviewed prior to being made
-  available through Google Play for Education. However, we will periodically
-  review updated apps for quality.
-</p>
-
-<p>
-  <strong>Does the app maturity rating reflect solely on what a user can do
-  within my Android app, or does the web version of my app influence the rating
-  as well?</strong>
-</p>
-
-<p>
-  The maturity rating that you set for an Android app refers only to the
-  content displayed in that app.
-</p>
-
-<div class="headerLine">
-  <h2 id="app-features">
-  App Features
-  </h2>
-
-
-</div>
-
-<p>
-  <strong>Do I need separate builds of my phone and tablet apps for Google Play
-  for Education, or is it the exact same app that lives on Google
-  Play?</strong>
-</p>
-
-<p>
-  We recommend you create one app and use it in both Google Play and Google
-  Play for Education.
-</p>
-
-<p>
-  <strong>What is the best way to get students’ work within apps sent back to
-  their teachers?</strong>
-</p>
-
-<p>
-  Teachers have mentioned that many apps achieve this by email from a third
-  party, which isn’t optimal for schools. As many schools use Google Apps for
-  Education, consider integrating your apps with Google Drive using the
-  <a href="https://developers.google.com/drive/about-sdk">SDK</a>.
-</p>
-
-<p>
-  <strong>How can developers test the teacher experience in Google Play for
-  Education? Is there a way to get an account to test it?</strong>
-</p>
-
-<p>
-  Currently, we are unable to provide developers with a test account to test
-  the Google Play for Education user experience. We’re investigating ways to
-  allow developers to simulate the environment.
-</p>
-
-<p>
-  <strong>If I already have an app in the Chrome Apps Pack will I get some help
-  migrating this to Android?</strong>
-</p>
-
-<p>
-  If you’d like to reach tablet users in schools we encourage you to build a
-  native app for the optimal user experience. Considerations for building your
-  apps can be found in the <a href=
-  "{@docRoot}distribute/essentials/gpfe-guidelines.html">Google Play for
-  Education Guidelines</a>.
-</p>
-
-<div class="headerLine">
-  <h2 id="marketing-and-roi">
-  Marketing and ROI
-  </h2>
-
-
-</div>
-
-<p>
-  <strong>What are you doing to promote these apps to educators?</strong>
-</p>
-
-<p>
-  Google Play for Education is an extension of Google Play targeting schools
-  and making the discovery of educational apps easier. It helps your apps gain
-  visibility with the right audiences, without having to knock on school doors.
-  We’re constantly referring to the highest quality apps in our educator
-  outreach. We’ve also developed a series of collections to help educators
-  quickly browse apps for the most common use cases.
-</p>
-
-<p>
-  <strong>How many installs have similar apps had on Google Play for Education?
-  How much can I expect to make if I do an ROI analysis?</strong>
-</p>
-
-<p>
-  While we cannot disclose specific numbers, Google Play app listings provide
-  app download ranges for all apps.
-</p>
-
-<p>
-  <strong>What is the seasonality like for the education market? What are the
-  key timing considerations for app developers?</strong>
-</p>
-
-<p>
-  In the United States, school districts’ budget decisions go through a
-  planning phase in the Spring with budgets being released on July 1. We’ve
-  observed high purchase-volumes in the second quarter of the calendar year, to
-  use up end-of-year budgets. New budget purchases begin in the third quarter
-  of the calendar year.
-</p>
-
-<p>
-  <strong>Is there a way to offer a special deal, such as a discount, only on
-  Google Play for Education and not on Google Play?</strong>
-</p>
-
-<p>
-  No, this isn’t possible. Pricing, including special offers, must be the same
-  between Google Play for Education and Google Play.
-</p>
-
-<div class="headerLine">
-  <h2 id="devices">
-  Devices
-  </h2>
-
-
-</div>
-
-<p>
-  <strong>Which devices are available in the program? Will more be
-  available?</strong>
-</p>
-
-<p>
-  Nexus 7 is available for shipment now, and the Asus Transformer, HP Slate 8
-  Pro, and Galaxy Tab for Education will be available in early 2014. We look
-  forward to welcoming more Android devices into the Google in Education family
-  soon.
-</p>
-
-<p>
-  <strong>Can the devices be shared among many students?</strong>
-</p>
-
-<p>
-  No. Currently, this program is for one-to-one use. Each student can login to
-  one specific tablet that is allocated to them.
-</p>
-
-<div class="headerLine">
-  <h2 id="accounts">
-  Accounts
-  </h2>
-
-
-</div>
-
-<p>
-  <strong>Will an app know whether a user is a teacher or student?</strong>
-</p>
-
-<p>
-  No, the app has no mechanism for knowing if it’s running on a teacher’s
-  device or a student’s device. We recommend developers use their own user
-  database to enable this feature, where logins can be based on Google Account
-  information.
-</p>
-
-<p>
-  <strong>What log-in method do you recommend for an app on Google Play for
-  Education?</strong>
-</p>
-
-<p>
-  One of the key pieces of feedback we’ve heard multiple times from various
-  schools is that they prefer apps that offer Google Single Sign-on, so that
-  teachers and students don’t need to remember multiple log-in credentials. As
-  schools in the program use Google Accounts and Google Apps for Education,
-  offering Google Single Sign-on is ideal.
-</p>
-<div class="headerLine"><h2 id="related-resources">Related Resources</h2></div>
-
-<div class="resource-widget resource-flow-layout col-13"
-  data-query="collection:distribute/toolsreference/gpfefaq"
-  data-sortOrder="-timestamp"
-  data-cardSizes="6x3,6x3,6x3,9x3,9x3,9x3"
-  data-maxResults="6"></div>
-
diff --git a/docs/html/distribute/googleplay/edu/start.jd b/docs/html/distribute/googleplay/edu/start.jd
index 4886b5a..136611c 100644
--- a/docs/html/distribute/googleplay/edu/start.jd
+++ b/docs/html/distribute/googleplay/edu/start.jd
@@ -1,4 +1,4 @@
-page.title=Get Started with Education
+page.title=Publish Android Apps for Education
 page.image=/distribute/images/play-education.jpg
 meta.tags="education", "guidelines", "quality"
 page.tags="education", "addendum"
@@ -16,21 +16,23 @@
 </ol>
 </div></div>
 <p>
-  If you've got great apps for education and want to reach even more teachers
+  If you've got great Android apps for education and want to reach even more teachers
   and students, you can join the <strong>Google Play for Education</strong>
   program in a few simple steps. You do everything using the familiar tools and
   processes in Google Play.
 </p>
 
 <p>
-  Note that Google Play for Education is currently available to <strong>K-12
-  schools in the United States</strong> only.
+Note that Google Play for Education is currently available to <strong>K-12 schools in the United
+States</strong> only.</p>
+
+<p>If you have an educational Chrome app instead of an Android app, you can learn more about
+Chrome Apps in Google Play for Education at <a href=
+"https://developers.google.com/edu">developers.google.com/edu</a>.
 </p>
 
-<div class="center-img">
-  <img src="{@docRoot}images/gpfe-start-0.jpg" style=
-  "border:1px solid #ddd;padding:0px;width:100%;">
-</div>
+<img src="{@docRoot}images/gpfe-start-0.jpg" style=
+  "border:1px solid #ddd;padding:0px" width="760" height="403">
 
 <div class="headerLine">
   <h2 id="register">
diff --git a/docs/html/distribute/googleplay/googleplay_toc.cs b/docs/html/distribute/googleplay/googleplay_toc.cs
index 4196c39..36e424a 100644
--- a/docs/html/distribute/googleplay/googleplay_toc.cs
+++ b/docs/html/distribute/googleplay/googleplay_toc.cs
@@ -25,13 +25,7 @@
   </li>
   <li class="nav-section">
     <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/edu/start.html">
-          <span class="en">Get Started with Education</span>
-        </a>
-    </div>
-  </li>
-  <li class="nav-section">
-    <div class="nav-section empty" style="font-weight:normal"><a href="<?cs var:toroot?>distribute/googleplay/edu/faq.html">
-          <span class="en">Education FAQ</span>
+          <span class="en">Publish Android Apps for Education</span>
         </a>
     </div>
   </li>
diff --git a/docs/html/google/gcm/ccs.jd b/docs/html/google/gcm/ccs.jd
index 03addfd..4389e3d 100644
--- a/docs/html/google/gcm/ccs.jd
+++ b/docs/html/google/gcm/ccs.jd
@@ -19,7 +19,11 @@
         <li><a href="#response">Response format</a></li>
       </ol>
       </li>
-  <li><a href="#upstream">Upstream Messages</a> </li>
+  <li><a href="#upstream">Upstream Messages</a>
+    <ol>
+      <li><a href="#receipts">Receive return receipts</a></li>
+    </ol>
+  </li>
   <li><a href="#flow">Flow Control</a> </li>
   <li><a href="#implement">Implementing an XMPP-based App Server</a>
     <ol class="toc">
@@ -43,9 +47,6 @@
 </div>
 </div>
 
-<p class="note"><strong>Note:</strong> To try out this feature, sign up using
-<a href="https://services.google.com/fb/forms/gcm/">this form</a>.</p>
-
 <p>The GCM Cloud Connection Server (CCS) is an XMPP endpoint that provides a
 persistent, asynchronous, bidirectional connection to Google servers. The
 connection can be used to send and receive messages between your server and
@@ -149,8 +150,8 @@
   <li>CCS adds the field {@code message_id}, which is required. This ID uniquely
 identifies the message in an XMPP connection. The ACK or NACK from CCS uses the
 {@code message_id} to identify a message sent from 3rd-party app servers to CCS.
-Therefore, it's important that this {@code message_id} not only be unique, but
-always present.</li>
+Therefore, it's important that this {@code message_id} not only be unique (per
+sender ID), but always present.</li>
 </ul>
 
 <p>In addition to regular GCM messages, control messages are sent, indicated by
@@ -188,7 +189,8 @@
           &quot;hello&quot;:&quot;world&quot;,
       }
       &quot;time_to_live&quot;:&quot;600&quot;,
-      &quot;delay_while_idle&quot;: true/false
+      &quot;delay_while_idle&quot;: true/false,
+      &quot;delivery_receipt_requested&quot;: true/false
   }
   &lt;/gcm&gt;
 &lt;/message&gt;
@@ -227,42 +229,48 @@
 <p>Below are some examples.</p>
 
 <p>Bad registration:</p>
+
 <pre>&lt;message&gt;
-  &lt;data:gcm xmlns:data=&quot;google:mobile:data&quot;&gt;
+  &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
   {
-    &quot;error&quot;:&quot;BAD_REGISTRATION&quot;,  // error code
+    &quot;message_type&quot;:&quot;nack&quot;,
     &quot;message_id&quot;:&quot;msgId1&quot;,
-    &quot;from&quot;:&quot;PA91bHFOtaQGSwupt5l1og&quot;,
-    &quot;message_type&quot;:&quot;nack&quot;
+    &quot;from&quot;:&quot;SomeInvalidRegistrationId&quot;,
+    &quot;error&quot;:&quot;BAD_REGISTRATION&quot;,
+    &quot;error_description&quot;:&quot;Invalid token on 'to' field: SomeInvalidRegistrationId&quot;
   }
-  &lt;/data:gcm&gt;
+  &lt;/gcm&gt;
 &lt;/message&gt;</pre>
 
-<p>Invalid "time to live":</p>
+<p>Invalid JSON:</p>
 
 <pre>&lt;message&gt;
-  &lt;data:gcm xmlns:data=&quot;google:mobile:data&quot;&gt;
-  {
-     &quot;error&quot;:&quot;InvalidJson : INVALID_TTL : Invalid value (-1) for \&quot;time_to_live\&quot;: must be between 0 and \&quot;2419200\&quot;\n&quot;,
-     &quot;message_id&quot;:&quot;msgId1&quot;,
-     &quot;from&quot;:&quot;APA91bHFOtaQGSwupt5l1og&quot;,
-     &quot;message_type&quot;:&quot;nack&quot;
-  }
-  &lt;/data:gcm&gt;
-&lt;/message&gt;</pre>
+ &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
+ {
+   &quot;message_type&quot;:&quot;nack&quot;,
+   &quot;message_id&quot;:&quot;msgId1&quot;,
+   &quot;from&quot;:&quot;APA91bHFOtaQGSwupt5l1og&quot;,
+   &quot;error&quot;:&quot;INVALID_JSON&quot;,
+   &quot;error_description&quot;:&quot;InvalidJson: JSON_TYPE_ERROR : Field \&quot;time_to_live\&quot; must be a JSON java.lang.Number: abc&quot;
+ }
+ &lt;/gcm&gt;
+&lt;/message&gt;
+</pre>
 
-<p>JSON type error:</p>
+<p>Quota exceeded:</p>
 
 <pre>&lt;message&gt;
-  &lt;data:gcm xmlns:data=&quot;google:mobile:data&quot;&gt;
-  {
-     &quot;error&quot;:&quot;InvalidJson : JSON_TYPE_ERROR : Field \&quot;delay_while_idle\&quot; must be a JSON java.lang.Boolean: not-boolean-user-supplied-value\n&quot;,
-     &quot;message_id&quot;:&quot;msgId1&quot;,
-     &quot;from&quot;:&quot;APA91bHFOtaQGSwupt5l1og&quot;,
-     &quot;message_type&quot;:&quot;nack&quot;
-  }
-  &lt;/data:gcm&gt;
-&lt;/message&gt;</pre>
+ &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
+ {
+   &quot;message_type&quot;:&quot;nack&quot;,
+   &quot;message_id&quot;:&quot;msgId1&quot;,
+   &quot;from&quot;:&quot;APA91bHFOtaQGSwupt5l1og&quot;,
+   &quot;error&quot;:&quot;QUOTA_EXCEEDED&quot;,
+   &quot;error_description&quot;:&quot;Short-term downstream quota exceeded for this registration id&quot;
+ }
+ &lt;/gcm&gt;
+&lt;/message&gt;
+</pre>
 
 
 <p>The following table lists NACK error codes. Unless otherwise
@@ -300,7 +308,7 @@
 </tr>
 <tr>
 <td>{@code INVALID_JSON}</td>
-<td>The JSON message payload was not valid.</td>
+<td>The JSON message payload is not valid.</td>
 </tr>
 <tr>
 <td>{@code QUOTA_EXCEEDED}</td>
@@ -309,10 +317,10 @@
 rate.</td>
 </tr>
 <tr>
-<td>{@code SERVICE_UNAVAILABLE}</td>
-<td>CCS is not currently able to process the message. The
-message should be retried over the same connection using exponential backoff
-with an initial delay of 1 second.</td>
+  <td>{@code SERVICE_UNAVAILABLE}</td>
+  <td>CCS is not currently able to process the message. The
+    message should be retried over the same connection using exponential backoff
+    with an initial delay of 1 second.</td>
 </tr>
 </table>
 
@@ -382,8 +390,8 @@
 // Bundle data consists of a key-value pair
 data.putString("hello", "world");
 // "time to live" parameter
-// This is optional. It specifies a value in seconds up to 4 weeks.
-int ttl = [0 seconds, 4 weeks]
+// This is optional. It specifies a value in seconds up to 24 hours.
+int ttl = [0 seconds, 24 hours]
 
 gcm.send(GCM_SENDER_ID + "&#64;gcm.googleapis.com", id, ttl, data);
 </pre>
@@ -419,6 +427,69 @@
   &lt;/gcm&gt;
 &lt;/message&gt;</pre>
 
+<h3 id="receipts">Receive return receipts</h3>
+
+<p>You can use upstream messaging to get receipt notifications, confirming
+that a given message was sent to a device. Your 3rd-party app server receives the receipt
+notification from CCS once the message has been sent to the device.</p>
+
+<p>To enable this feature, the message your 3rd-party app server sends to CCS must include
+a field called <code>&quot;delivery_receipt_requested&quot;</code>. When this field is set to
+<code>true</code>, CCS sends a return receipt. Here is an XMPP stanza containing a JSON
+message with <code>&quot;delivery_receipt_requested&quot;</code> set to <code>true</code>:</p>
+
+<pre>&lt;message id=&quot;&quot;&gt;
+  &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
+  {
+      &quot;to&quot;:&quot;REGISTRATION_ID&quot;,
+      &quot;message_id&quot;:&quot;m-1366082849205&quot;
+      &quot;data&quot;:
+      {
+          &quot;hello&quot;:&quot;world&quot;,
+      }
+      &quot;time_to_live&quot;:&quot;600&quot;,
+      &quot;delay_while_idle&quot;: true,
+      <strong>&quot;delivery_receipt_requested&quot;: true</strong>
+  }
+  &lt;/gcm&gt;
+&lt;/message&gt;
+</pre>
+
+<p>Here is an example of a receipt notification message that CCS sends back to your 3rd-party
+app server:</p>
+
+</p>
+<pre>&lt;message id=&quot;&quot;&gt;
+  &lt;gcm xmlns=&quot;google:mobile:data&quot;&gt;
+  {
+      &quot;category&quot;:&quot;com.example.yourapp&quot;, // to know which app sent it
+      &quot;data&quot;:
+      {
+         &#x201c;message_status&quot;:&quot;MESSAGE_SENT_TO_DEVICE&quot;,
+         &#x201c;original_message_id&#x201d;:&#x201d;m-1366082849205&#x201d;
+         &#x201c;device_registration_id&#x201d;: &#x201c;REGISTRATION_ID&#x201d;
+      },
+      &quot;message_id&quot;:&quot;dr2:m-1366082849205&quot;,
+      &quot;message_type&quot;:&quot;receipt&quot;,
+      &quot;from&quot;:&quot;gcm.googleapis.com&quot;
+  }
+  &lt;/gcm&gt;
+&lt;/message&gt;</pre>
+
+<p>Note the following:</p>
+
+<ul>
+  <li>The {@code &quot;message_type&quot;} is set to {@code &quot;receipt&quot;}.
+  <li>The {@code &quot;message_status&quot;} is set to {@code &quot;MESSAGE_SENT_TO_DEVICE&quot;},
+  indicating that the message was delivered. Notice that in this case,
+{@code &quot;message_status&quot;} is not a field but rather part of the data payload.</li>
+  <li>The receipt message ID consists of the original message ID, but with a
+<code>dr:</code> prefix. Your 3rd-party app server must send an ACK back with this ID,
+which in this example is {@code dr2:m-1366082849205}.</li>
+  <li>The original message ID and status are inside the
+{@code &quot;data&quot;} field.</li>
+</ul>
+
 <h2 id="flow">Flow Control</h2>
 
 <p>Every message sent to CCS receives either an ACK or a NACK response. Messages
diff --git a/docs/html/google/gcm/client.jd b/docs/html/google/gcm/client.jd
index ac446dc..20bff10 100644
--- a/docs/html/google/gcm/client.jd
+++ b/docs/html/google/gcm/client.jd
@@ -246,7 +246,8 @@
 <h3 id="sample-register">Register for GCM</h3>
 <p>An Android application needs to register with GCM servers before it can receive
 messages. When an app registers, it receives a registration ID, which it can then
-store for future use. In the following snippet the {@code onCreate()} method in the sample app's
+store for future use (note that registration IDs must be kept secret). In the
+following snippet the {@code onCreate()} method in the sample app's
 main activity checks to see if the app is already registered with GCM and with
 the server:</p>
 
diff --git a/docs/html/google/gcm/gcm.jd b/docs/html/google/gcm/gcm.jd
index 88bf659..19151b9 100644
--- a/docs/html/google/gcm/gcm.jd
+++ b/docs/html/google/gcm/gcm.jd
@@ -123,7 +123,7 @@
 it to the 3rd-party application server, which uses it to identify each device 
 that has registered to receive messages for a given Android application. In other words,
 a registration ID is tied to a particular Android application running on a particular
-device.
+device. Note that registration IDs must be kept secret.
 <br/>
 <br/>
 <strong>Note:</strong> If you use 
diff --git a/docs/html/google/gcm/index.jd b/docs/html/google/gcm/index.jd
index 70f7a9c..56e0865 100644
--- a/docs/html/google/gcm/index.jd
+++ b/docs/html/google/gcm/index.jd
@@ -14,7 +14,10 @@
   <h1 itemprop="name" style="margin-bottom:0;">Google Cloud Messaging for Android</h1>
   <p itemprop="description">
   Google Cloud Messaging for Android (GCM) is a service that allows you to send data
-from your server to your users' Android-powered device, and also to receive messages from devices on the same connection. The GCM service handles all aspects of queueing of messages and delivery to the target Android application running on the target device. GCM is completely free no matter how big your messaging needs are, and there are no quotas.
+from your server to your users' Android-powered device, and also to receive messages from
+devices on the same connection. The GCM service handles all aspects of queueing of messages
+and delivery to the target Android application running on the target device. GCM is
+completely free no matter how big your messaging needs are, and there are no quotas.
 </p>
 
 </div>
@@ -27,31 +30,39 @@
     <p>This could be a lightweight
 message telling your app there is new data to be fetched from the
 server (for instance, a movie uploaded by a friend), or it could be a message containing
-up to 4kb of payload data (so apps like instant messaging can consume the message directly). <a href="{@docRoot}google/gcm/gcm.html">GCM Architectural Overview.</a></p>
+up to 4kb of payload data (so apps like instant messaging can consume the message directly).
+<a href="{@docRoot}google/gcm/gcm.html">GCM Architectural Overview.</a></p>
 
     <h4>Send "send-to-sync" messages</h4>
-    <p>A send-to-sync (collapsible) message is often a "tickle" that tells a mobile application to sync data from the server. For example, suppose you have an email application. When a user receives new email on the server, the server pings the mobile application with a "New mail" message. This tells the application to sync to the server to pick up the new email.
-    <a href="{@docRoot}google/gcm/adv.html#s2s">Send-to-sync messages</a>.</p>
-    </a>
+    <p>A send-to-sync (collapsible) message is often a "tickle" that tells a mobile
+    application to sync data from the server. For example, suppose you have an email
+    application. When a user receives new email on the server, the server pings the mobile
+    application with a "New mail" message. This tells the application to sync to the server
+    to pick up the new email.
+    <a href="{@docRoot}google/gcm/adv.html#s2s">Learn more &raquo;</a></p>
 
     <h4>Send messages with payload</h4>
-    <p>Unlike a send-to-sync message, every "message with payload" (non-collapsible message) is delivered. The payload the message contains can be up to 4kb.
-    <a href="{@docRoot}google/gcm/adv.html#payload">Messages with payload</a>.</p>
+    <p>Unlike a send-to-sync message, every "message with payload" (non-collapsible message)
+    is delivered. The payload the message contains can be up to 4kb.
+    <a href="{@docRoot}google/gcm/adv.html#payload">Learn more &raquo;</a></p>
   </div>
 
 
   <div class="col-6 normal-links">
     <h3 style="clear:left">New Features</h3>
-    <h4>Faster, easier GCM setup</h4>
-    <p>Streamlined registration makes it simple and fast to add GCM support to your Android app. <a href="{@docRoot}google/gcm/gs.html">Learn more &raquo;</a></p>
-    <h4>Upstream messaging over XMPP</h4>
-    <p>GCM's Cloud Connection Service (CCS) lets you communicate with Android devices over a persistent XMPP connection. The primary advantages of CCS are speed, and the ability to receive upstream messages (that is, messages from a device to the cloud). You can use the service in tandem with existing GCM APIs. Use <a href="https://services.google.com/fb/forms/gcm/">this form</a> to sign up for CCS. <a href="{@docRoot}google/gcm/ccs.html">Learn more &raquo;</a></p>
 
-    <h4>Seamless multi-device messaging</h4>
-    <p>Maps a single user to a notification key, which you can then use to send a single message to multiple devices owned by the user. Use <a href="https://services.google.com/fb/forms/gcm/">this form</a> to sign up for User Notifications. <a href="{@docRoot}google/gcm/notifications.html">Learn more &raquo;</a></p>
+
+
+    <h4>Return Receipts</h4>
+    <p>You can use upstream messaging to get receipt notifications, confirming that a given
+    message was sent to a device. Your 3rd-party app server receives the receipt notification
+    from CCS once the message has been sent to the device.
+    <a href="{@docRoot}google/gcm/ccs.html#receipts">Learn more &raquo;</a></p>
+
 
    <h4>Get Started</h4>
-    <p>Get started using the new features with a tutorial that walks you through creating a GCM app. <a href="{@docRoot}google/gcm/gs.html">Learn more &raquo;</a></p>
+    <p>Get started with a tutorial that walks you through creating a GCM app.
+    <a href="{@docRoot}google/gcm/gs.html">Learn more &raquo;</a></p>
   </div>
 
 </div>
diff --git a/docs/html/google/gcm/notifications.jd b/docs/html/google/gcm/notifications.jd
index 43a7368..2815f3d 100644
--- a/docs/html/google/gcm/notifications.jd
+++ b/docs/html/google/gcm/notifications.jd
@@ -14,8 +14,8 @@
 <h2>In this document</h2>
 
 <ol class="toc">
-  <li><a href="#request">Request Format</a></li>
-  <li><a href="#create">Generate a Notification Key</a></li>
+  <li><a href="#gen-server">Generate a Notification Key on the Server</a></li>
+  <li><a href="#gen-client">Generate a Notification Key on the Client</a></li>
   <li><a href="#add">Add Registration IDs</a></li>
   <li><a href="#remove">Remove Registration IDs</a></li>
   <li><a href="#upstream">Send Upstream Messages</a></li>
@@ -31,15 +31,11 @@
 
 <ol class="toc">
 <li><a href="{@docRoot}google/gcm/gs.html">Getting Started</a></li>
-<li><a href="https://services.google.com/fb/forms/gcm/" class="external-link" target="_android">CCS and User Notifications Signup Form</a></li>
 </ol>
 
 </div>
 </div>
 
-<p class="note"><strong>Note:</strong> To try out this feature, sign up using <a href="https://services.google.com/fb/forms/gcm/">this form</a>.</p>
-
-
 <p>With user notifications, 3rd-party app servers can send a single message to
 multiple instance of an app running on devices owned by a single user. This feature
 is called <em>user notifications</em>. User notifications make it possible for every
@@ -76,27 +72,23 @@
 <p>You can use this feature with either the <a href="ccs.html">XMPP</a> (CCS) or
 <a href="http.html">HTTP</a> connection server.</p>
 
+<p>You can generate notification keys in two different ways: on the server, and on
+the client, if the user has a Google account. All of the associated registration IDs
+can be mapped to a single user.</p>
 
 <p>The examples below show you how to perform generate/add/remove operations,
 and how to send upstream messages. For generate/add/remove operations, the
 message body is JSON.</p>
 
-<h2 id="request">Request Format</h2>
-<p>To send a  message, the application server issues a POST request to
-<code>https://android.googleapis.com/gcm/notification</code>.</p>
+<h2 id="gen-server">Generate a Notification Key on the Server</h2>
 
-<p>Here is the HTTP request header you should use for all create/add/remove operations:</p>
-
-<pre>content-type: "application/json"
-Header : "project_id": &lt;projectID&gt;
-Header: "Authorization", "key=API_KEY"
-</pre>
-
-<h2 id="create">Generate a Notification Key</h2>
+<p>To generate a notification key on the server, you create a new
+create a new <code>notification_key</code> and map it to a
+<code>notification_key_name</code>.</p>
 
 <p>This example shows how to create a new <code>notification_key</code> for a
 <code>notification_key_name</code> called <code>appUser-Chris</code>.
-The {@code notification_key_name} is a name or identifier (can be a username for
+The {@code notification_key_name} is a name or identifier (it can be a username for
 a 3rd-party app) that is unique to a given user. It is used by third parties to
 group together registration IDs for a single user. Note that <code>notification_key_name</code>
 and <code>notification_key</code> are unique to a group of registration IDs. It is also
@@ -116,10 +108,109 @@
    &quot;registration_ids&quot;: [&quot;4&quot;, &quot;8&quot;, &quot;15&quot;, &quot;16&quot;, &quot;23&quot;, &quot;42&quot;]
 }</pre>
 
+<h3 id="request-server">Request format</h3>
+
+<p>To send a message in cases where your notification key is generated on the server,
+the application server issues a POST request to
+<code>https://android.googleapis.com/gcm/notification</code>.</p>
+
+<p>Here is the HTTP request header you should use for all server side create/add/remove operations:</p>
+
+<pre>content-type: "application/json"
+Header : "project_id": &lt;projectID&gt;
+Header: "Authorization", "key=API_KEY"
+</pre>
+
+
+<h2 id="gen-client">Generate a Notification Key on the Client</h2>
+
+<p>Generating a notification key on the client is useful for cases where a server is unavailable.
+To generate a notification key on the client, the device must have at least one
+Google account. Note that the process for generating a notification key on the client is significantly
+different from the server process described above.</p>
+
+<p>To generate a notification key on the client:</p>
+
+<ol>
+  <li>Open your project in the <a href="https://cloud.google.com/console">Google Developers Console</a>.</li>
+  <li>Click <strong>APIS &amp; AUTH &gt; Credentials</strong>.</li>
+  <li>Under OAuth, click <strong>Create new Client ID</strong>.</li>
+  <li>In the <strong>Create Client ID</strong> dialog, select <strong>Web Application</strong> as
+the application type, and click <strong>Create Client ID</strong>.</li>
+  <li>Copy the value from <strong>Client ID for web application &gt; Client ID</strong>.
+This client ID represents a Google account "scope" that you will use to generate an {@code id_token}.</li>
+</ol>
+
+<p>Once you've followed the above steps and gotten a client ID from Google Developers Console,
+ you're ready to add this feature to your app. First check the device for the presence of a Google
+account. For example:</p>
+
+<pre>// This snippet takes the simple approach of using the first returned Google account,
+// but you can pick any Google account on the device.
+public String getAccount() {
+    Account[] accounts = AccountManager.get(getActivity()).
+        getAccountsByType(&quot;com.google&quot;);
+    if (accounts.length == 0) {
+        return null;
+    }
+    return accounts[0].name;
+}</pre>
+
+<p>Next, get an authentication token ({@code id_token}) by using the <code><a href=
+"http://developer.android.com/reference/com/google/android/gms/auth/GoogleAuthUtil.html">GoogleAuthUtil</a></code>
+class. For example:</p>
+
+<pre>String accountName = getAccount();
+
+// Initialize the scope using the client ID you got from the Console.
+final String scope = &quot;audience:server:client_id:&quot;
+        + &quot;1262xxx48712-9qs6n32447mcj9dirtnkyrejt82saa52.apps.googleusercontent.com&quot;;
+String id_token = null;
+try {
+    id_token = GoogleAuthUtil.getToken(context, accountName, scope);
+} catch (Exception e) {
+    log(&quot;exception while getting id_token: &quot; + e);
+}
+...</pre>
+
+<p>Now use <code>id_token</code> to authenticate your request.
+This add operation returns a {@code notification_key}.
+Third parties must save this {@code notification_key} (as well as its mapping to the
+<code>notification_key_name</code>)
+to use in subsequent operations. Note that a client request only takes a single regID.
+The only operations supported on the client side are add/remove.</p>
+
+<pre>request:
+{
+   &quot;operation&quot;: &quot;add&quot;,
+   &quot;notification_key_name&quot;: &quot;appUser-Chris&quot;,
+   &quot;registration_ids&quot;: [&quot;4&quot;]
+   &quot;id_token&quot;: &quot;id_token&quot;
+}</pre>
+
+<h3 id="request-client">Request format</h3>
+
+<p>To send a message in cases where your notification key is generated on the client,
+the application server issues a POST request to
+<code>https://android.googleapis.com/gcm/googlenotification</code>.</p>
+
+<p>Here is the HTTP request header you should use for all add/remove operations. The
+client side doesn't support the create operation;
+the add operation has the effect of creating the notification key if it doesn't already
+exist:</p>
+
+<pre>content-type: "application/json"
+Header : "project_id": &lt;projectID&gt;
+</pre>
+
+<p>Note that the authentication token is passed in the JSON body as shown above, not the header.
+This is different from the server case.</p>
+
+
 <h2 id="add">Add Registration IDs</h2>
 
 <p>This example shows how to add registration IDs for a given notification key.
-The maximum number of members allowed for a {@code notification_key} is 10.</p>
+The maximum number of members allowed for a {@code notification_key} is 20.</p>
 
 <p>Note that the <code>notification_key_name</code> is not strictly required for
 adding/removing regIDs. But including it protects you against accidentally using
diff --git a/docs/html/google/gcm/server.jd b/docs/html/google/gcm/server.jd
index ccd1267..e3a6b25 100644
--- a/docs/html/google/gcm/server.jd
+++ b/docs/html/google/gcm/server.jd
@@ -120,7 +120,8 @@
   <li>Able to store the API key and client registration IDs. The
 API key is included in the header of POST requests that send
 messages.</li>
- <li>Able to generate message IDs to uniquely identify each message it sends.</li>
+ <li>Able to generate message IDs to uniquely identify each message it sends. Message IDs
+should be unique per sender ID.</li>
 </ul>
 
 <h2 id="send-msg">Sending Messages</h2>
diff --git a/docs/html/google/gcs/gcs-signup.jd b/docs/html/google/gcs/gcs-signup.jd
new file mode 100644
index 0000000..7334cec
--- /dev/null
+++ b/docs/html/google/gcs/gcs-signup.jd
@@ -0,0 +1,10 @@
+page.title=Sign Up for Google Cloud Save
+
+@jd:body
+
+<p>Sign up to be a trial partner for Google Cloud Save.</p>
+
+
+<iframe src="https://docs.google.com/a/google.com/forms/d/1_V67YIXzLDLb-UzxOOpSjUDuJFfeYg3hEUT0oliK2ck/viewform?embedded=true" width="100%" height="930" frameborder="0" marginheight="0" marginwidth="0" id="signupform">Loading...</iframe>
+</body>
+</html>
diff --git a/docs/html/google/gcs/index.jd b/docs/html/google/gcs/index.jd
new file mode 100644
index 0000000..e5f4776
--- /dev/null
+++ b/docs/html/google/gcs/index.jd
@@ -0,0 +1,30 @@
+page.title=Google Cloud Save
+page.tags="gcs"
+header.hide=1
+@jd:body
+
+
+<div class="landing-banner">
+
+<div class="col-5" style="min-height:100px">
+  <img src="{@docRoot}images/google/gcs.png" />
+</div>
+<div class="col-7">
+
+  <h1 itemprop="name" style="margin-bottom:0;">Google Cloud Save</h1>
+  <p itemprop="description">
+  Google Cloud Save is a service that enables per-user data storage
+and sync in your apps with no backend programming required. Google Cloud Save
+stores its data
+in <a href="http://developers.google.com/datastore/">Google Cloud Datastore</a>,
+ a fully managed, schemaless database for storing non-relational data. Cloud
+Datastore automatically scales with your users.
+Google Cloud Save works even when your device is offline, and it
+provides an easy transition to server-side coding because
+the same database is accessible via App Engine and Compute Engine.
+Finally, Google Cloud Save provides a generous initial per-user free quota that
+expands as your user base grows.
+</p>
+<a href="{@docRoot}google/gcs/gcs-signup.html" class="button">Sign Up</a>
+</div>
+</div>
diff --git a/docs/html/google/index.jd b/docs/html/google/index.jd
index 2e97d62..4778a85 100644
--- a/docs/html/google/index.jd
+++ b/docs/html/google/index.jd
@@ -89,6 +89,16 @@
     to use Google Cloud Messaging.</p>
 </div>
 
+<div class="landing-cell">
+  <div class="cell-icon">
+  <img src="{@docRoot}images/google/gcs-small.png" width="40" >
+  </div>
+    <h4><a href="{@docRoot}google/gcs/index.html"
+    >Google Cloud Save</a></h4>
+    <p>Enable per-user data storage and sync in your apps with no backend programming
+    required.</p>
+</div>
+
 </div><!-- col-6 -->
 
 
diff --git a/docs/html/google/play-services/games.jd b/docs/html/google/play-services/games.jd
index 94f6715..a73f688 100644
--- a/docs/html/google/play-services/games.jd
+++ b/docs/html/google/play-services/games.jd
@@ -1,4 +1,5 @@
 page.title=Google Play Game Services
+page.tags="games"
 header.hide=1
 
 @jd:body
diff --git a/docs/html/google/play-services/index.jd b/docs/html/google/play-services/index.jd
index f5d4574..eec4f5a 100644
--- a/docs/html/google/play-services/index.jd
+++ b/docs/html/google/play-services/index.jd
@@ -11,10 +11,8 @@
 <div class="col-6">
 
   <h1 itemprop="name" style="margin-bottom:0;">Google Play Services</h1>
-  <p itemprop="description">
-  </p>
-
-  <p>Give your apps more features to attract users on a wider range of devices.
+  <p itemprop="description">Give your apps more features to attract users
+  on a wider range of devices.
   With Google Play services, your app can take advantage
   of the latest, Google-powered features such as Maps, Google+, and more,
   with automatic platform updates distributed as an APK through
diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs
index ff08312..ea36405 100644
--- a/docs/html/guide/guide_toc.cs
+++ b/docs/html/guide/guide_toc.cs
@@ -373,6 +373,9 @@
             <li><a href="<?cs var:toroot ?>guide/topics/media/mediarouteprovider.html">
                   <span class="en">Media Route Provider</span></a>
                 </li>
+            <li><a href="<?cs var:toroot ?>guide/topics/media/exoplayer.html">
+                  <span class="en">ExoPlayer</span></a>
+                </li>
             <li><a href="<?cs var:toroot ?>guide/appendix/media-formats.html">
                    <span class="en">Supported Media Formats</span></a>
                 </li>
diff --git a/docs/html/guide/practices/verifying-apps-art.jd b/docs/html/guide/practices/verifying-apps-art.jd
index 0eedfaf..8a88222 100644
--- a/docs/html/guide/practices/verifying-apps-art.jd
+++ b/docs/html/guide/practices/verifying-apps-art.jd
@@ -66,7 +66,7 @@
 href="{@docRoot}/tools/debugging/debugging-memory.html#LogMessages"><code>GC_FOR_ALLOC</code></a>-type
 occurrences or to reduce fragmentation. You can verify which runtime is in use
 by calling {@link java.lang.System#getProperty(java.lang.String)
-System.getProperty("dalvik.vm.version")}. If ART is in use, the property's value
+System.getProperty("java.vm.version")}. If ART is in use, the property's value
 is <code>"2.0.0"</code> or higher.</p>
 
 <p>Furthermore, a compacting garbage collector is under development in the <a
diff --git a/docs/html/guide/topics/manifest/activity-element.jd b/docs/html/guide/topics/manifest/activity-element.jd
index b648d48..c4d5083 100644
--- a/docs/html/guide/topics/manifest/activity-element.jd
+++ b/docs/html/guide/topics/manifest/activity-element.jd
@@ -5,7 +5,8 @@
 
 <dl class="xml">
 <dt>syntax:</dt>
-<dd><pre class="stx">&lt;activity android:<a href="#reparent">allowTaskReparenting</a>=["true" | "false"]
+<dd><pre class="stx">&lt;activity android:<a href="#embedded">allowEmbedded</a>=["true" | "false"]
+          android:<a href="#reparent">allowTaskReparenting</a>=["true" | "false"]
           android:<a href="#always">alwaysRetainTaskState</a>=["true" | "false"]
           android:<a href="#clear">clearTaskOnLaunch</a>=["true" | "false"]
           android:<a href="#config">configChanges</a>=["mcc", "mnc", "locale",
@@ -62,6 +63,17 @@
 
 <dt>attributes:</dt>
 <dd><dl class="attr">
+<dt><a name="embedded"></a>{@code android:allowEmbedded}</dt>
+<dd>
+    Indicate that the activity can be launched as the embedded child of another
+    activity. Particularly in the case where the child lives in a container
+    such as a Display owned by another activity. For example, activities
+    that are used for Wear custom notifications must declare this so
+    Wear can display the activity in it's context stream, which resides
+    in another process.
+
+    <p>The default value of this attribute is <code>false</code>.
+</dd>
 <dt><a name="reparent"></a>{@code android:allowTaskReparenting}</dt>
 <dd>Whether or not the activity can move from the task that started it to
 the task it has an affinity for when that task is next brought to the
diff --git a/docs/html/guide/topics/media/exoplayer.jd b/docs/html/guide/topics/media/exoplayer.jd
new file mode 100644
index 0000000..17b4669
--- /dev/null
+++ b/docs/html/guide/topics/media/exoplayer.jd
@@ -0,0 +1,514 @@
+page.title=ExoPlayer
+page.tags="audio","video","adaptive","streaming","DASH","smoothstreaming"
+@jd:body
+
+<div id="qv-wrapper">
+  <div id="qv">
+    <h2>In this document</h2>
+    <ol>
+      <li><a href="#overview">Overview</a></li>
+      <li><a href="#trackrenderer">TrackRenderer</a></li>
+      <li><a href="#samplesource">SampleSource</a>
+        <ol>
+          <li><a href="#mediaextractor">Providing media using MediaExtractor</a></li>
+          <li><a href="#adaptive-playback">Providing media for adaptive playback</a>
+            <ol>
+              <li><a href="#format-selection">Format selection for adaptive playback</a></li>
+            </ol>
+          </li>
+        </ol>
+      <li><a href="#events">Player Events</a>
+        <ol>
+          <li><a href="#high-events">High level events</a></li>
+          <li><a href="#low-events">Low level events</a></li>
+        </ol>
+      </li>
+      <li><a href="#sending-messages">Sending messages to components</a></li>
+      <li><a href="#customizing">Customizing ExoPlayer</a>
+        <ol>
+          <li><a href="#custom-guidelines">Custom component guidelines</a></li>
+        </ol>
+      </li>
+      <li><a href="#drm">Digital Rights Management</a></li>
+    </ol>
+    <h2>Key Classes</h2>
+    <ol>
+      <li>{@link android.media.MediaCodec}</li>
+      <li>{@link android.media.MediaExtractor}</li>
+      <li>{@link android.media.AudioTrack}</li>
+    </ol>
+    <h2>Related Samples</h2>
+    <ol>
+      <li><a class="external-link" href="https://github.com/google/ExoPlayer">
+        ExoPlayer Project</a></li>
+      <li><a class="external-link" href="http://google.github.io/ExoPlayer/doc/reference/packages.html">
+        Class Reference</a></li>
+    </ol>
+  </div>
+</div>
+
+
+<p>Playing videos and music is a popular activity on Android devices. The Android framework
+  provides {@link android.media.MediaPlayer} as a quick solution for playing media with minimal
+  code, and the {@link android.media.MediaCodec} and {@link android.media.MediaExtractor} classes
+  are provided for building custom media players. The open source project, ExoPlayer, is a
+  solution between these two options, providing a pre-built player that you can extend.</p>
+
+<p>ExoPlayer supports features not currently provided by
+  {@link android.media.MediaPlayer}, including Dynamic adaptive streaming
+  over HTTP (DASH), SmoothStreaming, and persistent caching. ExoPlayer can be extended
+  to handle additional media formats, and because you include it as part of your app code,
+  you can update it along with your app.</p>
+
+<p>This guide describes how to use ExoPlayer for playing Android supported media formats, as well as
+  DASH and SmoothStreaming playback. This guide also discusses ExoPlayer events, messages, DRM
+  support and guidelines for customizing the player.</p>
+
+<p class="note">
+  <strong>Note:</strong> ExoPlayer is an open source project that is not part of the Android
+  framework and is distributed separately from the Android SDK. The project contains a library and
+  a demo app that shows both simple and more advanced use of ExoPlayer:</p>
+
+<ul>
+    <li><a class="external-link" href="https://github.com/google/ExoPlayer/tree/master/library">
+      ExoPlayer Library</a> &mdash; This part of the project contains the core library classes.</li>
+    <li><a class="external-link" href="https://github.com/google/ExoPlayer/tree/master/demo/src/main/java/com/google/android/exoplayer/demo/simple">
+      Simple Demo</a> &mdash; This part of the app demonstrates a basic use of ExoPlayer.</li>
+    <li><a class="external-link" href="https://github.com/google/ExoPlayer/tree/master/demo/src/main/java/com/google/android/exoplayer/demo/full">
+      Full Demo</a> &mdash; This part of the app demonstrates more advanced features,
+      including the ability to select between multiple audio tracks, a background audio mode,
+      event logging and DRM protected playback. </li>
+</ul>
+
+
+<h2 id="overview">Overview</h2>
+
+<p>ExoPlayer is a media player built on top of the {@link android.media.MediaExtractor} and
+  {@link android.media.MediaCodec} APIs released in Android 4.1 (API level 16). At the core of this
+  library is the {@code ExoPlayer} class. This class maintains the player’s global state, but makes few
+  assumptions about the nature of the media being played, such as how the media data is obtained,
+  how it is buffered or its format. You inject this functionality through ExoPlayer’s {@code
+  prepare()} method in the form of {@code TrackRenderer} objects.</p>
+
+<p>ExoPlayer provides default {@code TrackRenderer} implementations for audio and
+  video, which make use of the {@link android.media.MediaCodec} and {@link android.media.AudioTrack}
+  classes in the Android framework. Both renderers require a {@code SampleSource} object, from which
+  they obtain individual media samples for playback. Figure 1 shows the high level object model for
+  an ExoPlayer implementation configured to play audio and video using these components.</p>
+
+<img src="{@docRoot}images/exoplayer/object-model.png" alt="" id="figure1" />
+<p class="img-caption">
+  <strong>Figure 1.</strong> High level object model for an ExoPlayer configured to play audio
+  and video using {@code TrackRenderer} objects
+</p>
+
+
+<h2 id="trackrenderer">TrackRenderer</h2>
+
+<p>A {@code TrackRenderer} processes a component of media for playback, such as
+  video, audio or text. The ExoPlayer class invokes methods on its {@code TrackRenderer} instances from a
+  single playback thread, and by doing so causes each media component to be rendered as the global
+  playback position is advanced. The ExoPlayer library provides {@code MediaCodecVideoTrackRenderer} as
+  the default implementations rendering video and {@code MediaCodecAudioTrackRenderer} for audio.
+  Both implementations make use of {@link android.media.MediaCodec} to decode individual media
+  samples. They can handle all audio and video formats supported by a given Android device
+  (see <a href="http://developer.android.com/guide/appendix/media-formats.html">Supported Media
+  Formats</a> for details). The ExoPlayer library also provides an implementation for rendering
+  text called {@code TextTrackRenderer}.
+</p>
+
+<p>The code example below outlines the main steps required to instantiate an ExoPlayer to play video
+  and audio using the standard {@code TrackRenderer} implementations.</p>
+
+<pre>
+// 1. Instantiate the player.
+player = ExoPlayer.Factory.newInstance(RENDERER_COUNT);
+// 2. Construct renderers.
+MediaCodecVideoTrackRenderer videoRenderer = …
+MediaCodecAudioTrackRenderer audioRenderer = ...
+// 3. Inject the renderers through prepare.
+player.prepare(videoRenderer, audioRenderer);
+// 4. Pass the surface to the video renderer.
+player.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE,
+        surface);
+// 5. Start playback.
+player.setPlayWhenReady(true);
+...
+player.release(); // Don’t forget to release when done!
+</pre>
+
+<p>For a complete example, see the {@code SimplePlayerActivity} in the ExoPlayer demo app, which
+  correctly manages an ExoPlayer instance with respect to both the {@link android.app.Activity} and
+  {@link android.view.Surface} lifecycles.</p>
+
+
+<h2 id="samplesource">SampleSource</h2>
+
+<p>A standard {@code TrackRenderer} implementation requires a {@code SampleSource} to
+  be provided in its constructor. A {@code SampleSource} object provides format information and
+  media samples to be rendered. The ExoPlayer library provides {@code FrameworkSampleSource} and
+  {@code ChunkSampleSource}. The {@code FrameworkSampleSource} class uses {@link
+  android.media.MediaExtractor} to request, buffer and extract the media samples. The {@code
+  ChunkSampleSource} class provides adaptive playback using DASH or SmoothStreaming, and
+  implements networking, buffering and media extraction within the ExoPlayer library.</p>
+
+
+<h3 id="mediaextractor">Providing media using MediaExtractor</h3>
+
+<p>
+  In order to render media formats supported by the Android framework, the {@code
+  FrameworkSampleSource} class uses {@link android.media.MediaExtractor} for networking,
+  buffering and sample extraction functionality. By doing so, it supports any media container format
+  supported by the version of Android where it is running. For more information about media formats
+  supported by Android, see <a href="{@docRoot}guide/appendix/media-formats.html">Supported
+  Media Formats</a>.
+</p>
+
+<p>The diagram in Figure 2 shows the object model for an ExoPlayer implementation using
+  {@code FrameworkSampleSource}.</p>
+
+<img src="{@docRoot}images/exoplayer/frameworksamplesource.png" alt="" id="figure2" />
+<p class="img-caption">
+  <strong>Figure 2.</strong> Object model for an implementation of ExoPlayer that renders
+  media formats supported by Android using {@code FrameworkSampleSource}
+</p>
+
+<p>The following code example outlines how the video and audio renderers are constructed to
+  load the video from a specified URI.</p>
+
+<pre>
+FrameworkSampleSource sampleSource = new FrameworkSampleSource(
+        activity, uri, null, 2);
+MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(
+        sampleSource, null, true, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT, 0,
+        mainHandler, playerActivity, 50);
+MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(
+        sampleSource, null, true);
+</pre>
+
+<p>The ExoPlayer demo app provides a complete implementation of this code in
+  {@code DefaultRendererBuilder}. The {@code SimplePlaybackActivity} class uses it to play one
+  of the videos available in the demo app. Note that in the example, video and audio
+  are muxed, meaning they are streamed together from a single URI. The {@code FrameworkSampleSource}
+  instance provides video samples to the {@code videoRenderer} object and audio samples to the
+  {@code audioRenderer} object as they are extracted from the media container format. It is also
+  possible to play demuxed media, where video and audio are streamed separately from different URIs.
+  This functionality can be achieved by having two {@code FrameworkSampleSource} instances instead
+  of one.</p>
+
+
+<h3 id="adaptive-playback">Providing media for adaptive playback</h3>
+
+<p>ExoPlayer supports adaptive streaming, which allows the quality of the
+  media data to be adjusted during playback based on the network conditions. DASH
+  and SmoothStreaming are examples of adaptive streaming technologies. Both these approaches
+  load media in small chunks (typically 2 to 10 seconds in duration). Whenever a chunk of media
+  is requested, the client selects from a number of possible formats. For example, a client may
+  select a high quality format if network conditions are good, or a low quality format if network
+  conditions are bad. In both techniques, video and audio are streamed separately.</p>
+
+<p>ExoPlayer supports adaptive playback through use of the {@code ChunkSampleSource} class,
+  which loads chunks of media data from which individual samples can be extracted. Each {@code
+  ChunkSampleSource} requires a {@code ChunkSource} object to be injected through its constructor,
+  which is responsible for providing media chunks from which to load and read samples. The {@code
+  DashMp4ChunkSource} and {@code SmoothStreamingChunkSource} classes provide DASH and SmoothStreaming
+  playback using the FMP4 container format. The {@code DashWebMChunkSource} class uses the WebM
+  container format to provide DASH playback.</p>
+
+<p>All of the standard {@code ChunkSource} implementations require a {@code FormatEvaluator} and
+  a {@code DataSource} to be injected through their constructors. The {@code FormatEvaluator}
+  objects select from the available formats before each chunk is loaded. The {@code DataSource}
+  objects are responsible for actually loading the data. Finally, the {@code ChunkSampleSources}
+  require a {@code LoadControl} object that controls the chunk buffering policy.</p>
+
+<p>The object model of an ExoPlayer configured for a DASH adaptive playback is shown in the
+  diagram below. This example uses an {@code HttpDataSource} object to stream the media over the
+  network. The video quality is varied at runtime using the adaptive implementation of {@code
+  FormatEvaluator}, while audio is played at a fixed quality level.</p>
+
+<img src="{@docRoot}images/exoplayer/adaptive-streaming.png" alt="" id="figure3" />
+<p class="img-caption">
+  <strong>Figure 3.</strong> Object model for a DASH adaptive playback using ExoPlayer
+</p>
+
+<p>The following code example outlines how the video and audio renderers are constructed.</p>
+
+<pre>
+Handler mainHandler = playerActivity.getMainHandler();
+LoadControl loadControl = new DefaultLoadControl(
+        new BufferPool(BUFFER_SEGMENT_SIZE));
+BandwidthMeter bandwidthMeter = new BandwidthMeter();
+
+// Build the video renderer.
+DataSource videoDataSource = new HttpDataSource(userAgent,
+        HttpDataSource.REJECT_PAYWALL_TYPES, bandwidthMeter);
+ChunkSource videoChunkSource = new DashMp4ChunkSource(videoDataSource,
+        new AdaptiveEvaluator(bandwidthMeter), videoRepresentations);
+ChunkSampleSource videoSampleSource = new ChunkSampleSource(videoChunkSource,
+        loadControl, VIDEO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true);
+MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(
+        videoSampleSource, null, true, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
+        0, mainHandler, playerActivity, 50);
+
+// Build the audio renderer.
+DataSource audioDataSource = new HttpDataSource(userAgent,
+        HttpDataSource.REJECT_PAYWALL_TYPES, bandwidthMeter);
+ChunkSource audioChunkSource = new DashMp4ChunkSource(audioDataSource,
+        new FormatEvaluator.FixedEvaluator(), audioRepresentation);
+SampleSource audioSampleSource = new ChunkSampleSource(audioChunkSource,
+        loadControl, AUDIO_BUFFER_SEGMENTS * BUFFER_SEGMENT_SIZE, true);
+MediaCodecAudioTrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(
+        audioSampleSource, null, true);
+</pre>
+
+<p>In this code, {@code videoRepresentations} and {@code audioRepresentation} are {@code
+  Representation} objects, each of which describes one of the available media streams. In the DASH
+  model, these streams are parsed from a media presentation description (MPD) file. The ExoPlayer
+  library provides a {@code MediaPresentationDescriptionParser} class to obtain {@code
+  Representation} objects from MPD files.</p>
+
+<p class="note">
+  <strong>Note:</strong> Building Representation objects from MPD files is not required. You can
+  build Representation objects from other data sources if necessary.
+</p>
+
+<p>The ExoPlayer demo app provides complete implementation of this code in
+  {@code DashVodRendererBuilder}. The {@code SimplePlaybackActivity} class uses this builder to
+  construct renderers for playing DASH sample videos in the demo app. It asynchronously fetches a
+  specified MPD file in order to construct the required {@code Representation} objects. For an
+  equivalent SmoothStreaming example, see the {@code SmoothStreamingRendererBuilder} class in the
+  demo app.</p>
+
+
+<h4 id="format-selection">Format selection for adaptive playback</h4>
+
+<p>For DASH and SmoothStreaming playback, consider both static format selection at the
+  start of playback and dynamic format selection during playback. Static format selection should be
+  used to filter out formats that should not be used throughout the playback, for example formats
+  with resolutions higher than the maximum supported by the playback device. Dynamic selection varies
+  the selected format during playback, typically to adapt video quality in response to changes in
+  network conditions.</p>
+
+<h5 id="static-selection">Static format selection</h5>
+
+<p>When preparing a player, you should consider filtering out some of the available formats if
+  they are not useable for playback. Static format selection allows you to filter out
+  formats that cannot be used on a particular device or are not compatible with your player.
+  For audio playback, this often means picking a single format to play and discarding the others.</p>
+
+<p>For video playback, filtering formats can be more complicated. Apps should first
+  eliminate any streams that whose resolution is too high to be played by the device. For H.264,
+  which is normally used for DASH and SmoothStreaming playback, ExoPlayer’s {@code MediaCodecUtil}
+  class provides a {@code maxH264DecodableFrameSize()} method that can be used to determine what
+  resolution streams the device is able to handle, as shown in the following code example:</p>
+
+<pre>
+int maxDecodableFrameSize = MediaCodecUtil.maxH264DecodableFrameSize();
+Format format = representation.format;
+if (format.width * format.height &lt;= maxDecodableFrameSize) {
+  // The device can play this stream.
+  videoRepresentations.add(representation);
+} else {
+  // The device isn't capable of playing this stream.
+}
+</pre>
+
+<p>This approach is used to filter {@code Representations} in the {@code DashVodRendererBuilder}
+  class of the ExoPlayer demo app, and similarly to filter track indices in {@code
+  SmoothStreamingRendererBuilder}.</p>
+
+<p>In addition to eliminating unsupported formats, it should be noted that the ability to
+  seamlessly switch between H.264 streams of different resolution is an optional decoder feature
+  available in Android 4.3 (API level 16) and higher, and so is not supported by all devices. The
+  availability of an adaptive H.264 decoder can be queried using {@code MediaCodecUtil}, as shown in
+  the following code example:</p>
+
+<pre>
+boolean isAdaptive = MediaCodecUtil.getDecoderInfo(MimeTypes.VIDEO_H264).adaptive;
+</pre>
+
+<p>The {@code MediaCodecVideoTrackRenderer} class is still able to handle resolution changes on
+  devices that do not have adaptive decoders, however the switch is not seamless. Typically, the
+  switch creates a small discontinuity in visual output lasting around 50-100ms. For devices that
+  do not provide an adaptive decoder, app developers may choose to adapt between formats at
+  a single fixed resolution so as to avoid discontinuities. The ExoPlayer demo app
+  implementation does not pick a fixed resolution.</p>
+
+
+<h5 id="dynamic-selection">Dynamic format selection</h5>
+
+<p>During playback, you can use a {@code FormatEvaluator} to dynamically select from the
+  available video formats. The ExoPlayer library provides a {@code FormatEvaluator.Adaptive}
+  implementation for dynamically selecting between video formats based on the current network
+  conditions.</p>
+
+<p>This class provides a simple, general purpose reference implementation, however you are
+  encouraged to write your own {@code FormatEvaluator} implementation to best suit your particular
+  needs.</p>
+
+
+<h2 id="events">Player Events</h2>
+
+<p>During playback, your app can listen for events generated by the ExoPlayer that indicate the
+  overall state of the player. These events are useful as triggers for updating the app user
+  interface such as playback controls. Many ExoPlayer components also report their own component
+  specific low level events, which can be useful for performance monitoring.</p>
+
+
+<h3 id="high-events">High level events</h3>
+
+<p>ExoPlayer allows instances of {@code ExoPlayer.Listener} to be added and removed using its
+  {@code addListener()} and {@code removeListener()} methods. Registered listeners are notified of
+  changes in playback state, as well as when errors occur that cause playback to fail. For more
+  information about the valid playback states and the possible transitions between them, see the
+  ExoPlayer source code.</p>
+
+<p>Developers who implement custom playback controls should register a listener and use it to
+  update their controls as the player’s state changes. An app should also show an
+  appropriate error to the user if playback fails.</p>
+
+<h3 id="low-events">Low level events</h3>
+
+<p>In addition to high level listeners, many of the individual components provided by the
+  ExoPlayer library allow their own event listeners. For example, {@code
+  MediaCodecVideoTrackRenderer} has constructors that take a {@code
+  MediaCodecVideoTrackRenderer.EventListener}. In the ExoPlayer demo app, {@code SimplePlayerActivity}
+  acts as a listener so that it can adjust the dimensions of the target surface to have the correct
+  height and width ratio for the video being played:</p>
+
+<pre>
+&#64;Override
+public void onVideoSizeChanged(int width, int height) {
+  surfaceView.setVideoWidthHeightRatio(height == 0 ? 1 : (float) width / height);
+}
+</pre>
+
+<p>The {@code RendererBuilder} classes in the ExoPlayer demo app inject the activity as the
+  listener, for example in the {@code DashVodRendererBuilder} class:</p>
+
+<pre>
+MediaCodecVideoTrackRenderer videoRenderer = new MediaCodecVideoTrackRenderer(
+        videoSampleSource, null, true, MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT,
+        0, <strong>mainHandler, playerActivity</strong>, 50);
+</pre>
+
+<p>Note that you must pass a {@link android.os.Handler} object to the renderer, which determines
+  the thread on which the listener’s methods are invoked. In most cases, you should use a
+  {@link android.os.Handler} associated with the app’s main thread, as is the case in this example.
+  </p>
+
+<p>Listening to individual components can be useful for adjusting UI based on player events, as
+  in the example above. Listening to component events can also be helpful for logging performance
+  metrics. For example, {@code MediaCodecVideoTrackRenderer} notifies its listener of dropped video
+  frames. A developer may wish to log such metrics to track playback performance in their
+  app.</p>
+
+<p>Many components also notify their listeners when errors occur. Such errors may or may not
+  cause playback to fail. If an error does not cause playback to fail, it may still result in
+  degraded performance, and so you may wish to log all errors in order to track playback
+  performance. Note that an ExoPlayer instance always notifies its high level listeners of errors that
+  cause playback to fail, in addition to the listener of the individual component from which the error
+  originated. Hence, you should display error messages to users only from high level listeners.
+  Within individual component listeners, you should use error notifications only for informational
+  purposes.</p>
+
+
+<h2 id="sending-messages">Sending messages to components</h2>
+
+<p>Some ExoPlayer components allow changes in configuration during playback. By convention, you make
+  these changes by passing asynchronous messages through the ExoPlayer to the component.
+  This approach ensures both thread safety and that the configuration change is
+  executed in order with any other operations being performed on the player.</p>
+
+<p>The most common use of messaging is passing a target surface to
+  {@code MediaCodecVideoTrackRenderer}:</p>
+
+<pre>
+player.sendMessage(videoRenderer, MediaCodecVideoTrackRenderer.MSG_SET_SURFACE,
+        surface);
+</pre>
+
+<p>Note that if the surface needs to be cleared because
+  {@link android.view.SurfaceHolder.Callback#surfaceDestroyed
+  SurfaceHolder.Callback.surfaceDestroyed()} has been invoked, then you must send this
+  message using the blocking variant of {@code sendMessage()}:</p>
+<p>
+
+<pre>
+player.blockingSendMessage(videoRenderer,
+        MediaCodecVideoTrackRenderer.MSG_SET_SURFACE, null);
+</pre>
+
+<p>You must use a blocking message because the contract of {@link
+  android.view.SurfaceHolder.Callback#surfaceDestroyed surfaceDestroyed()} requires that the
+  app does not attempt to access the surface after the method returns. The {@code
+  SimplePlayerActivity} class in the demo app demonstrates how the surface should be set and
+  cleared.</p>
+
+
+<h2 id="customizing">Customizing ExoPlayer</h2>
+
+<p>One of the main benefits of ExoPlayer over {@link android.media.MediaPlayer} is the ability to
+  customize and extend the player to better suit the developer’s use case. The ExoPlayer library
+  is designed specifically with this in mind, defining a number of abstract base classes and
+  interfaces that make it possible for app developers to easily replace the default implementations
+  provided by the library. Here are some use cases for building custom components:</p>
+
+<ul>
+  <li><strong>{@code TrackRenderer}</strong> - You may want to implement a custom
+    {@code TrackRenderer} to handle media types other than audio and video. The {@code
+    TextTrackRenderer} class within the ExoPlayer library is an example of how to implement a
+    custom renderer. You could use the approach it demonstrates to render custom
+    overlays or annotations. Implementing this kind of functionality as a {@code TrackRenderer}
+    makes it easy to keep the overlays or annotations in sync with the other media being played.</li>
+  <li><strong>{@code SampleSource}</strong> - If you need to support a container format not
+    already handled by {@link android.media.MediaExtractor} or ExoPlayer, consider implementing a
+    custom {@code SampleSource} class.</li>
+  <li><strong>{@code FormatEvaluator}</strong> - The ExoPlayer library provides {@code
+    FormatEvaluator.Adaptive} as a simple reference implementation that switches between different
+    quality video formats based on the available bandwidth. App developers are encouraged to
+    develop their own adaptive {@code FormatEvaluator} implementations, which can be designed to
+    suit their use specific needs.</li>
+  <li><strong>{@code DataSource}</strong> - ExoPlayer’s upstream package already contains a
+    number of {@code DataSource} implementations for different use cases, such as writing and
+    reading to and from a persistent media cache. You may want to implement you own
+    {@code DataSource} class to load data in another way, such as a custom
+    protocol or HTTP stack for data input.</li>
+</ul>
+
+
+<h3 id="custom-guidelines">Custom component guidelines</h3>
+
+<p>If a custom component needs to report events back to the app, we recommend that you
+  do so using the same model as existing ExoPlayer components, where an event listener is passed
+  together with a {@link android.os.Handler} to the constructor of the component.</p>
+
+<p>We recommended that custom components use the same model as existing ExoPlayer components to
+  allow reconfiguration by the app during playback, as described in
+  <a href="#sending-messages">Sending messages to components</a>.
+  To do this, you should implement a {@code ExoPlayerComponent} and receive
+  configuration changes in its {@code handleMessage()} method. Your app should pass
+  configuration changes by calling ExoPlayer’s {@code sendMessage()} and {@code
+  blockingSendMessage()} methods.</p>
+
+
+<h2 id="drm">Digital Rights Management</h2>
+
+<p>On Android 4.3 (API level 18) and higher, ExoPlayer supports Digital Rights Managment (DRM)
+  protected playback. In order to play DRM protected content with ExoPlayer, your app must
+  inject a {@code DrmSessionManager} into the {@code MediaCodecVideoTrackRenderer} and {@code
+  MediaCodecAudioTrackRenderer} constructors. A {@code DrmSessionManager} object is responsible for
+  providing the {@code MediaCrypto} object required for decryption, as well as ensuring that the
+  required decryption keys are available to the underlying DRM module being used.</p>
+
+<p>The ExoPlayer library provides a default implementation of {@code DrmSessionManager}, called
+  {@code StreamingDrmSessionManager}, which uses {@link android.media.MediaDrm}. The session
+  manager supports any DRM scheme for which a modular DRM component exists on the device. All
+  Android devices are required to support Widevine modular DRM (with L3 security, although many
+  devices also support L1). Some devices may support additional schemes such as PlayReady.</p>
+
+<p>The {@code StreamingDrmSessionManager} class requires a {@code MediaDrmCallback} to be
+  injected into its constructor, which is responsible for actually making provisioning and key
+  requests. You should implement this interface to make network requests to your license
+  server and obtain the required keys. The {@code WidevineTestMediaDrmCallback} class in the
+  ExoPlayer demo app sends requests to a Widevine test server.</p>
diff --git a/docs/html/images/exoplayer/adaptive-streaming.png b/docs/html/images/exoplayer/adaptive-streaming.png
new file mode 100644
index 0000000..9fc650c9
--- /dev/null
+++ b/docs/html/images/exoplayer/adaptive-streaming.png
Binary files differ
diff --git a/docs/html/images/exoplayer/frameworksamplesource.png b/docs/html/images/exoplayer/frameworksamplesource.png
new file mode 100644
index 0000000..fd1e314
--- /dev/null
+++ b/docs/html/images/exoplayer/frameworksamplesource.png
Binary files differ
diff --git a/docs/html/images/exoplayer/object-model.png b/docs/html/images/exoplayer/object-model.png
new file mode 100644
index 0000000..e0d6e55
--- /dev/null
+++ b/docs/html/images/exoplayer/object-model.png
Binary files differ
diff --git a/docs/html/images/google/datastore-logo.png b/docs/html/images/google/datastore-logo.png
new file mode 100644
index 0000000..a0fc0a0
--- /dev/null
+++ b/docs/html/images/google/datastore-logo.png
Binary files differ
diff --git a/docs/html/images/google/gcs-small.png b/docs/html/images/google/gcs-small.png
new file mode 100644
index 0000000..b8dbe0d
--- /dev/null
+++ b/docs/html/images/google/gcs-small.png
Binary files differ
diff --git a/docs/html/images/google/gcs.png b/docs/html/images/google/gcs.png
new file mode 100644
index 0000000..7355d64
--- /dev/null
+++ b/docs/html/images/google/gcs.png
Binary files differ
diff --git a/docs/html/images/gpfe-start-0.jpg b/docs/html/images/gpfe-start-0.jpg
index e97381d..bb68aaa 100644
--- a/docs/html/images/gpfe-start-0.jpg
+++ b/docs/html/images/gpfe-start-0.jpg
Binary files differ
diff --git a/docs/html/images/home/auto.png b/docs/html/images/home/auto.png
new file mode 100644
index 0000000..7ea01b0
--- /dev/null
+++ b/docs/html/images/home/auto.png
Binary files differ
diff --git a/docs/html/images/home/tv.png b/docs/html/images/home/tv.png
new file mode 100644
index 0000000..3cf2034
--- /dev/null
+++ b/docs/html/images/home/tv.png
Binary files differ
diff --git a/docs/html/images/home/wear.png b/docs/html/images/home/wear.png
new file mode 100644
index 0000000..dfaded7
--- /dev/null
+++ b/docs/html/images/home/wear.png
Binary files differ
diff --git a/docs/html/images/tools/android-studio.png b/docs/html/images/tools/android-studio.png
deleted file mode 100644
index 4d93a86..0000000
--- a/docs/html/images/tools/android-studio.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/images/tools/laptop-studio.png b/docs/html/images/tools/laptop-studio.png
new file mode 100644
index 0000000..3684ff0
--- /dev/null
+++ b/docs/html/images/tools/laptop-studio.png
Binary files differ
diff --git a/docs/html/images/tools/wizard2.png b/docs/html/images/tools/wizard2.png
new file mode 100644
index 0000000..921d58c
--- /dev/null
+++ b/docs/html/images/tools/wizard2.png
Binary files differ
diff --git a/docs/html/images/tools/wizard3.png b/docs/html/images/tools/wizard3.png
new file mode 100644
index 0000000..d5749d5
--- /dev/null
+++ b/docs/html/images/tools/wizard3.png
Binary files differ
diff --git a/docs/html/images/tools/wizard4.png b/docs/html/images/tools/wizard4.png
new file mode 100644
index 0000000..2709f7b
--- /dev/null
+++ b/docs/html/images/tools/wizard4.png
Binary files differ
diff --git a/docs/html/images/tools/wizard5.png b/docs/html/images/tools/wizard5.png
new file mode 100644
index 0000000..105cf2a
--- /dev/null
+++ b/docs/html/images/tools/wizard5.png
Binary files differ
diff --git a/docs/html/images/tools/wizard6.png b/docs/html/images/tools/wizard6.png
new file mode 100644
index 0000000..8b0691c
--- /dev/null
+++ b/docs/html/images/tools/wizard6.png
Binary files differ
diff --git a/docs/html/images/tools/wizard7.png b/docs/html/images/tools/wizard7.png
new file mode 100644
index 0000000..83396cf
--- /dev/null
+++ b/docs/html/images/tools/wizard7.png
Binary files differ
diff --git a/docs/html/index.jd b/docs/html/index.jd
index de2980b..2f01538 100644
--- a/docs/html/index.jd
+++ b/docs/html/index.jd
@@ -7,24 +7,42 @@
 @jd:body
 
 <!-- Top full-bleed carousel -->
-<div class="home-new-carousel-1">
+<div class="home-new-carousel-1" style="margin-top:40px">
   <div class="fullscreen-carousel-content">
     <div class="vcenter">
       <div class="wrap clearfix">
-        <div class="resource-widget resource-flow-layout wrap col-16"
+
+        <div class="static resource-flow-layout wrap col-16">
+          <div class="resource resource-card resource-card-18x6">
+            <div class="card-bg" style="background-image: url('/preview/images/l-dev-prev.png');"></div>
+            <div class="card-info">
+              <div class="section"></div>
+              <div class="title">Android L Developer Preview</div>
+              <div class="description ellipsis" style="height: 285px;">
+                <div class="text" style="height: auto;">
+                  <p style="font-size:16px;">Get an early look at the next Android release and
+                    start using new APIs so your apps are ready when the platform officially launches.</p>
+                  <p>
+                  <a href="{@docRoot}preview/index.html" class="landing-button landing-secondary">Learn more</a></p>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        <div class="resource-widget resource-flow-layout wrap col-16 no-section"
           data-query="collection:index/primary"
           data-resourceStyle="card"
           data-sortOrder="-timestamp"
-          data-numStacks="3"
-          data-maxResults="4"
-          data-cardSizes="18x6,6x2,6x2,6x2">
+          data-maxResults="3"
+          data-cardSizes="6x2,6x2,6x2">
         </div> <!-- end .resource-widget -->
       </div> <!-- end .wrap -->
     </div> <!-- end .vcenter -->
   </div> <!-- end .fullscreen-carousel-content -->
 </div> <!-- end .fullscreen-carousel -->
 
-<div class="actions-bar">
+<div class="actions-bar" style="margin-top:30px">
   <div class="wrap">
     <div class="actions">
       <div><a href="{@docRoot}sdk/index.html">Get the SDK</a></div>
@@ -38,57 +56,46 @@
 
 
 <div class="landing-rest-of-page">
-  <div class="landing-section" style="background-color:#f5f5f5">
+  <div class="landing-section">
     <div class="wrap">
       <div class="landing-section-header">
-        <div class="landing-h1">Android, Everywhere You Need It</div>
-        <div class="landing-subhead">
-          Android runs on hundreds of millions of handheld devices around the world, <br /> and it now supports these exciting, new form-factors.
+        <div class="landing-h1">Develop for Multiple Form Factors</div>
+        <div class="landing-subhead" style="margin-top: 20px;">
+          Android runs on hundreds of millions of handheld devices around the world, <br />
+          and it now supports these exciting, new form-factors.
         </div>
       </div>
-      <div class="landing-body">
+      <div class="landing-body" style="margin-top: 80px;">
         <div class="landing-breakout cols">
-
-         <!-- <div class="resource-widget resource-flow-layout col-16" data-query="collection:index/devices"
-          data-sortorder="" data-cardsizes="6x6, 6x6, 6x6" data-maxresults="3"></div>-->
-
           <div class="col-3-wide">
-            <img src="" alt="">
-
-            <p>Wear</p>
-            <p class="landing-small">
-              Provide information on-the-go for your users, whenever they need it.
+              <img src="{@docRoot}images/home/wear.png">
+              <p class="landing-small" style="margin-top:30px">
+                Provide information on-the-go for your users, whenever they need it.
             </p>
             <p class="landing-small">
-              <a href="{@docRoot}wear">Learn about Android Wear</a>
+              <a href="{@docRoot}wear/index.html">Learn about Android Wear</a>
             </p>
           </div>
-
-          <div class="col-3-wide">
-            <img src="" alt="">
-
-            <p>TV</p>
+          <div class="col-3-wide">            
+             <img src="{@docRoot}images/home/tv.png">
+              <p class="landing-small" style="margin-top:30px">
+                Build your apps for the big screen and bring your content to life.
+              </p>
             <p class="landing-small">
-              Build your apps for the big screen and bring your content to life.
-            </p>
-            <p class="landing-small">
-              <a href="{@docRoot}tv">Learn about Android TV</a>
+              <a href="{@docRoot}tv/index.html">Learn about Android TV</a>
+
             </p>
           </div>
-
-          <div class="col-3-wide">
-            <img src="" alt="">
-
-            <p>Auto</p>
+          <div class="col-3-wide">            
+              <img src="{@docRoot}images/home/auto.png">
+              <p class="landing-small" style="margin-top:30px">
+                Extend your music apps to automobile
+                entertainment systems.
+             </p>
             <p class="landing-small">
-              Extend your music apps to automobile
-              entertainment systems.
-            </p>
-            <p class="landing-small">
-              <a href="{@docRoot}auto">Learn about Android Auto</a>
+              <a href="{@docRoot}auto/index.html">Learn about Android Auto</a>
             </p>
           </div>
-
         </div>
       </div>
     </div>  <!-- end .wrap -->
diff --git a/docs/html/jd_collections.js b/docs/html/jd_collections.js
index ca19c02..a92236e 100644
--- a/docs/html/jd_collections.js
+++ b/docs/html/jd_collections.js
@@ -2,10 +2,9 @@
   "index/primary": {
     "title": "",
     "resources": [
-      "preview/index.html",
-      "preview/material/index.html",
-      "preview/material/index.html",
-      "preview/material/index.html" 
+      "distribute/essentials/quality/tablets.html",
+      "distribute/engage/game-services.html",
+      "distribute/googleplay/edu/about.html" 
     ]
   },
   "index/devices": {
@@ -46,14 +45,14 @@
     "resources": [
       "distribute/googleplay/edu/about.html",
       "distribute/googleplay/edu/start.html",
-      "distribute/googleplay/edu/faq.html"
+      "https://developers.google.com/edu/faq"
     ]
   },
   "distribute/essentials": {
     "resources": [
       "distribute/essentials/quality/core.html",
       "distribute/essentials/quality/tablets.html",
-      "distribute/essentials/gpfe-guidelines.html",
+      "https://developers.google.com/edu/guidelines",
       "distribute/essentials/optimizing-your-app.html",
       "distribute/essentials/best-practices/apps.html",
       "distribute/essentials/best-practices/games.html"
@@ -166,16 +165,18 @@
     "title": "About Google Play for Education / Developers",
     "resources": [
       "distribute/googleplay/edu/start.html",
-      "distribute/essentials/gpfe-guidelines.html",
-      "distribute/googleplay/edu/faq.html",
-      "distribute/essentials/quality/tablets.html"
+      "https://developers.google.com/edu/guidelines",
+      "https://developers.google.com/edu/faq",
+      "distribute/essentials/quality/tablets.html",
+      "https://developers.google.com/edu/",
+      "https://www.google.com/edu/tablets/#tablets-family"
     ]
   },
   "distribute/googleplay/gpfe/dev": {
     "title": "About Google Play for Education / Developers",
     "resources": [
       "distribute/googleplay/edu/about.html",
-      "distribute/essentials/gpfe-guidelines.html",
+      "https://developers.google.com/edu/guidelines",
       "distribute/essentials/quality/tablets.html",
       "distribute/googleplay/developer-console.html",
       "http://play.google.com/about/developer-distribution-agreement-addendum.html",
@@ -207,7 +208,7 @@
     "resources": [
       "distribute/googleplay/developer-console.html",
       "distribute/googleplay/edu/start.html",
-      "distribute/googleplay/edu/faq.html"
+      "https://developers.google.com/edu/faq"
     ]
   },
   "distribute/essentials/eduessentials/educators": {
@@ -587,7 +588,7 @@
     "resources": [
       "distribute/essentials/quality/core.html",
       "distribute/essentials/quality/tablets.html",
-      "distribute/essentials/gpfe-guidelines.html"
+      "https://developers.google.com/edu/guidelines"
     ]
   },
   "distribute/toolsreference/launchchecklist/rating": {
diff --git a/docs/html/jd_extras.js b/docs/html/jd_extras.js
index d8db5bf..03bbea1 100644
--- a/docs/html/jd_extras.js
+++ b/docs/html/jd_extras.js
@@ -15,6 +15,17 @@
 
 DISTRIBUTE_RESOURCES = DISTRIBUTE_RESOURCES.concat([
   {
+    "title":"Android L Developer Preview",
+    "titleFriendly":"",
+    "summary":"<p style='font-size:18px;'>Get an early look at the next release and get your apps ready when the platform officially launches.</p>",
+    "url":"preview/index.html",
+    "group":"",
+    "keywords": [],
+    "tags": [],
+    "image":"preview/images/l-dev-prev.png",
+    "type":""
+  },
+  {
     "title":"Developer Registration",
     "titleFriendly":"",
     "summary":"Additional information about the registration process.",
@@ -208,7 +219,7 @@
     "keywords": [],
     "type": "support",
     "titleFriendly": ""
-  },   
+  },
   {
     "lang": "en",
     "group": "",
@@ -571,7 +582,7 @@
     "image":"http://chart.googleapis.com/chart?chl=GL%201.1%20only%7CGL%202.0%7CGL%203.0&chf=bg%2Cs%2C00000000&chd=t%3A0.1%2C93.5%2C6.4&chco=c4df9b%2C6fad0c&chs=400x250&cht=p",
     "lang":"en",
     "type":"about"
-  }, 
+  },
   {
     "lang": "en",
     "group": "",
@@ -700,7 +711,7 @@
     "timestamp": 1383243492000,
     "image": "http://i1.ytimg.com/vi/vzvpcEffvaE/maxresdefault.jpg",
     "title": "Introducing Google Play for Education",
-    "summary": "Google Play for Education is a destination where schools can find great, teacher-approved, educational apps and videos on Play Store. Teachers can filter content by subject matter, grade and other criteria. Bulk purchase and instant distribution let educators bring your apps directly to classrooms and schools.",
+    "summary": "Google Play for Education is a destination where schools can find great, teacher-approved, educational content&mdash;from videos and books, to educational apps&mdash;all in one place. Teachers can filter content by subject matter, grade and other criteria. Bulk purchase and instant distribution let educators bring your apps directly to classrooms and schools.",
     "keywords": [],
     "type": "youtube",
     "titleFriendly": ""
@@ -1128,5 +1139,57 @@
     "keywords": ["analytics, user behavior"],
     "type": "sdk",
     "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://developers.google.com/edu/guidelines",
+    "timestamp": null,
+    "image": "http://developer.android.com/distribute/images/edu-guidelines.jpg",
+    "title": "Education Guidelines",
+    "summary": "These guidelines and requirements help you develop great apps for students, which offer compelling content and an intuitive user experience on Android tablets.",
+    "keywords": [],
+    "type": "",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://developers.google.com/edu/faq",
+    "timestamp": null,
+    "image": "http://developer.android.com/distribute/images/gpfe-faq.jpg",
+    "title": "Education FAQ",
+    "summary": "Answers to common questions you might have about Google Play for Education.",
+    "keywords": [],
+    "type": "",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://developers.google.com/edu/",
+    "timestamp": null,
+    "image": "https://developers.google.com/edu/images/home-android.png",
+    "title": "Chrome Apps in Google Play for Education",
+    "summary": "Find out more about Chrome apps in Google Play for Education.",
+    "keywords": [],
+    "type": "",
+    "titleFriendly": ""
+  },
+  {
+    "lang": "en",
+    "group": "",
+    "tags": [],
+    "url": "https://www.google.com/edu/tablets/#tablets-family",
+    "timestamp": null,
+    "image": "https://www.google.com/edu/images/tablets/big-tablet.png",
+    "title": "Google Play for Education Tablets",
+    "summary": "Google Play for Education leverages a diverse set up tablets approved for the classroom which may help inform you how to build educational apps.",
+    "keywords": [],
+    "type": "",
+    "titleFriendly": ""
   }
-]); 
\ No newline at end of file
+]);
\ No newline at end of file
diff --git a/docs/html/jd_tag_helpers.js b/docs/html/jd_tag_helpers.js
index b0fe67a..7538e4d 100644
--- a/docs/html/jd_tag_helpers.js
+++ b/docs/html/jd_tag_helpers.js
@@ -64,6 +64,7 @@
 // Type lookups
 
 var ALL_RESOURCES_BY_TYPE = {
+  'about': ABOUT_RESOURCES,
   'design': DESIGN_RESOURCES,
   'distribute': DISTRIBUTE_RESOURCES,
   'google': GOOGLE_RESOURCES,
@@ -79,6 +80,7 @@
 // Tag lookups
 
 var ALL_RESOURCES_BY_TAG = mergeMaps(
+  {map:ABOUT_BY_TAG,arr:ABOUT_RESOURCES},
   {map:DESIGN_BY_TAG,arr:DESIGN_RESOURCES},
   {map:DISTRIBUTE_BY_TAG,arr:DISTRIBUTE_RESOURCES},
   {map:GOOGLE_BY_TAG,arr:GOOGLE_RESOURCES},
diff --git a/docs/html/preview/api-overview.jd b/docs/html/preview/api-overview.jd
index 40618a3..20764be 100644
--- a/docs/html/preview/api-overview.jd
+++ b/docs/html/preview/api-overview.jd
@@ -1,4 +1,4 @@
-page.title=L Developer Preview APIs
+page.title=API Overview
 excludeFromSuggestions=true
 sdk.platform.apiLevel=20
 @jd:body
@@ -15,9 +15,10 @@
 <ol id="toc44" class="hide-nested">
   <li><a href="#Behaviors">Important Behavior Changes</a>
     <ol>
+      <li><a href="#ART">New Android Runtime (ART)</a></li>
       <li><a href="#BehaviorNotifications">If your app implements notifications...</a></li>
-      <li><a href="#BehaviorFullscreen">If your app uses fullScreenIntent...</a></li>
-      <li><a href="#BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</a></li>
+      <li><a href="#BehaviorMediaControl">If your app uses RemoteControlClient...</a></li>
+<li><a href="#BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</a></li>
     </ol>
   </li>
   <li><a href="#UI">User Interface</a>
@@ -25,7 +26,7 @@
       <li><a href="#MaterialDesign">Material design support</a></li>
       <li><a href="#LockscreenNotifications">Lockscreen notifications</a></li>
       <li><a href="#NotificationsMetadata">Notifications metadata</a></li>
-      <li><a href="#Recents">Concurrent documents and activities in Recents screen</a></li>
+      <li><a href="#Recents">Concurrent documents and activities in the Recents screen</a></li>
       <li><a href="#WebView">WebView updates</a></li>
     </ol>
   </li>
@@ -37,11 +38,12 @@
   <li><a href="#Animations">Animation &amp; Graphics</a>
     <ol>
       <li><a href="#OpenGLES-3-1">Support for OpenGL ES 3.1</a></li>
+      <li><a href="#AndroidExtensionPack">Android Extension Pack</a></li>
     </ol>
   </li>
   <li><a href="#Multimedia">Multimedia</a>
     <ol>
-      <li><a href="#Camera-v2">Camera V2</a></li>
+      <li><a href="#Camera-v2">Camera API for advanced camera capabilities</a></li>
       <li><a href="#AudioPlayback">Audio playback</a></li>
       <li><a href="#MediaPlaybackControl">Media playback control</a></li>
     </ol>
@@ -53,25 +55,26 @@
   </li>
   <li><a href="#Wireless">Wireless and Connectivity</a>
     <ol>
-      <li><a href="#Multinetwork">Dynamic network selection and seamless handoff</a></li>
+      <li><a href="#Multinetwork">Multiple network connections</a></li>
       <li><a href="#BluetoothBroadcasting">Bluetooth broadcasting</a></li>
-      <li><a href="#NFCEnhancements">NFC enhancements for payments</a></li>
+      <li><a href="#NFCEnhancements">NFC enhancements</a></li>
     </ol>
   </li>
   <li><a href="#Power">Power Efficiency</a>
     <ol>
       <li><a href="#JobScheduler">Scheduling Jobs</a></li>
-      <li><a href="#PowerMeasurementTools">Developer tools and APIs for power measurement</a>
+      <li><a href="#PowerMeasurementTools">Developer tools for power measurement</a>
     </ol>
   </li>
   <li><a href="#Enterprise">Enterprise</a>
     <ol>
       <li><a href="#ManagedProvisioning">Managed provisioning</a></li>
+      <li><a href="#TaskLocking">Task locking</a></li>
     </ol>
   </li>
   <li><a href="#Printing">Printing Framework</a>
     <ol>
-      <li><a href="#PDFRender">PDF rendering</a></li>
+      <li><a href="#PDFRender">Render PDF as bitmap</a></li>
     </ol>
   </li>
   <li><a href="#TestingA11y">Testing &amp; Accessibility</a>
@@ -82,99 +85,178 @@
   <li><a href="#Manifest">Manifest Declarations</a>
     <ol>
       <li><a href="#ManifestFeatures">Declarable required features</a></li>
-      <li><a href="#ManifestPermissions">User permissions</a></li>
     </ol>
   </li>
 </ol>
 
-<h2>See also</h2>
-<ol>
-<li><a href="{@docRoot}sdk/api_diff/20/changes.html">API
-Differences Report &raquo;</a> </li>
-</ol>
-
 </div>
 </div>
 
-<p>L is an upcoming release for the Android platform
-that offers new features for users and app developers. This document provides
-an introduction to the most notable new APIs.</p>
+<p>The L Developer Preview gives you an advance look at the upcoming release
+for the Android platform, which offers new features for users and app
+developers. This document provides an introduction to the most notable APIs.</p>
 
-<p>L is currently available as a <strong>developer preview</strong> intended
-for early adopters and testers. If you are interested in influencing the
-direction of the Android framework,
+<p>The L Developer Preview is intended for <strong>developer early
+adopters</strong> and <strong>testers</strong>. If you are interested in
+influencing the direction of the Android framework,
 <a href="{@docRoot}preview/setup-sdk.html">give the L Developer Preview a
 try</a> and send us your feedback!</p>
 
-<p class="caution"><strong>Caution:</strong>You should not publish apps
-using L Developer Preview to the Google Play store.</p>
+<p class="caution"><strong>Caution:</strong> Do not not publish apps
+that use the L Developer Preview to the Google Play store.</p>
+
+<p class="note"><strong>Note:</strong> This document often refers to classes and
+methods that do not yet have reference material available on <a
+href="{@docRoot}">developer.android.com</a>. These API elements are
+formatted in {@code code style} in this document (without hyperlinks). For the
+preliminary API documentation for these elements, download the <a
+href="{@docRoot}preview/l-developer-preview-reference.zip">preview
+reference</a>.</p>
 
 <h2 id="Behaviors">Important Behavior Changes</h2>
 
 <p>If you have previously published an app for Android, be aware that your app
-  might be affected by changes in L.</p>
+  might be affected by changes in the upcoming release.</p>
+
+<h3 id="ART">New Android Runtime (ART)</h3>
+
+<p>The 4.4 release introduced a new, experimental Android runtime, ART. Under
+4.4, ART was optional, and the default runtime remained Dalvik. With the L
+Developer Preview, ART is now the default runtime.</p>
+
+<p>For an overview of ART's new features, see
+<a href="https://source.android.com/devices/tech/dalvik/art.html">Introducing
+ART</a>. Some of the major new features are:</p>
+
+<ul>
+  <li>Ahead-of-Time (AOT) compilation</li>
+  <li>Improved garbage collection (GC)</li>
+  <li>Improved debugging support</li>
+</ul>
+
+<p>Most Android apps should just work without change under ART. However, some
+techniques that work on Dalvik do not work on ART. For information about the
+most important issues, see
+<a href="{@docRoot}guide/practices/verifying-apps-art.html">Verifying App
+Behavior on the Android Runtime (ART)</a>. Pay particular attention if:</p>
+
+<ul>
+  <li>Your app uses Java Native Interface (JNI) to run C/C++ code.</li>
+  <li>You use development tools that generate non-standard code (such as some
+      obfuscators).</li>
+  <li>You use techniques that are incompatible with compacting garbage
+      collection. (ART does not currently implement compacting GC, but
+      compacting GC is under development in the Android Open-Source
+      Project.)</li>
+</ul>
 
 <h3 id="BehaviorNotifications">If your app implements notifications...</h3>
 
-<p>Notifications will be drawn with dark text atop white (or very light)
+<p>Notifications are drawn with dark text atop white (or very light)
 backgrounds to match the new material design widgets. Make sure that all your
-notifications look right with the new color scheme. You should remove or update
-assets and text styles that involve color. The system will automatically invert
-action icons in notifications. Use
-{@code android.app.Notification.Builder.setColor()} to set an accent color
-in a circle behind your {@code Notification.icon} image.</p>
+notifications look right with the new color scheme:</p>
 
-<p>The system will ignore all non-alpha channels in action icons and the main
-notification icon, so you should assume that these icons will be alpha-only.
-</p>
+<div class="figure" style="width:320px">
+  <img src="images/hun-example.png"
+    srcset="images/hun-example@2x.png 2x"
+    alt="" width="320" height="541" id="figure1" />
+  <p class="img-caption">
+    <strong>Figure 1.</strong> Fullscreen activity showing a heads-up notification
+  </p>
+</div>
+
+<ul>
+
+  <li>Update or remove assets that involve color.</li>
+
+  <li>The system automatically inverts action icons in notifications. Use
+  {@code android.app.Notification. Builder.setColor()} to set an accent color
+  in a circle behind your {@link android.app.Notification#icon} image.</li>
+
+  <li>The system ignores all non-alpha channels in action icons and the main
+  notification icon. You should assume that these icons are alpha-only.</li>
+
+</ul>
 
 <p>If you are currently adding sounds and vibrations to your notifications by
 using the {@link android.media.Ringtone}, {@link android.media.MediaPlayer},
-or {@link android.os.Vibrator} classes, make sure to remove this code so that
-the system can present notifications correctly in Do not disturb mode. You
-should use the {@link android.app.Notification.Builder} methods instead to add
-sounds and vibration.
-</p>
+or {@link android.os.Vibrator} classes, remove this code so that
+the system can present notifications correctly in Do
+not Disturb mode. Instead, use the {@link android.app.Notification.Builder}
+methods instead to add sounds and vibration.</p>
+
+<p>Notifications now appear in a small floating window
+(also called a <em>heads-up notification</em>) when the device is active
+(that is, the device is unlocked and its screen is on). These notifications
+appear similar to the compact form of your notification, except that the
+heads-up notification also shows action buttons. Users can act on, or dismiss,
+a heads-up notification without leaving the current app.</p>
+
+<p>Examples of conditions that may trigger heads-up notifications include:</p>
+
+<ul>
+  <li>The user's activity is in fullscreen mode (the app uses
+{@link android.app.Notification#fullScreenIntent}), or</li>
+  <li>The notification has high priority and uses ringtones or
+    vibrations</li>
+</ul>
+
+<p>If your app implements notifications under those scenarios, make sure that
+heads-up notifications are presented correctly.</p>
 
 <h3 id="BehaviorMediaControl">If your app uses RemoteControlClient...</h3>
 
-<p>Lockscreens in L will not show transport controls for your
-{@link android.media.RemoteControlClient}. Instead, your app can provide
-media playback control from the lockscreen through a media notification. This
+<p>Lockscreens in the L Developer Preview do not show transport controls for
+your {@link android.media.RemoteControlClient}. Instead, your app can provide
+media playback control from the lockscreen through a notification. This
 gives your app more control over the presentation of media buttons, while
 providing a consistent experience for users across the lockscreen and
 unlocked device.</p>
 
-<p>You must call {@code Notification.Builder.setVisibility(Notification.VISIBILITY_PUBLIC)} to mark your media notification as safe to reveal, even when the lockscreen is secured
-with a PIN, pattern, or password.</p>
+<p>The L Developer Preview introduces a new
+{@code android.app.Notification.MediaStyle} template which is recommended for
+this purpose. {@code MediaStyle} converts notification actions that you added
+with
+{@link android.app.Notification.Builder#addAction(int, java.lang.CharSequence,
+  android.app.PendingIntent)
+Notification.Builder.addAction()} into compact buttons embedded in your app's
+media playback notifications.</p>
 
-<h3 id="BehaviorFullscreen">If your app uses fullScreenIntent...</h3>
+<p>If you are using the new
+{@code android.media.session.MediaSession} class
+(see <a href="#MediaPlaybackControl">Media Playback Control</a> below), attach
+your session token with {@code Notification.MediaStyle.setMediaToken()} to
+inform the system that this notification controls an ongoing media session.</p>
 
-<p>Notifications now appear in a small floating window if all these conditions
-are met: the user’s activity is in fullscreen mode, the screen is on, and the
-device is unlocked. If your app implements fullscreen activities, make sure that
-these heads-up notifications are presented correctly.</p>
+<p>Call {@code
+Notification.Builder.setVisibility(Notification.VISIBILITY_PUBLIC)} to mark a
+notification as safe to show atop any lockscreen (secure or otherwise). For more
+information, see <a href="#LockscreenNotifications">Lockscreen Notifications</a>.</p>
 
 <h3 id="BehaviorGetRecentTasks">If your app uses ActivityManager.getRecentTasks()...</h3>
 
-<p>With the introduction of the new document tasks feature in L (see below),
-the {@code android.app.ActivityManager.getRecentTasks()} method is now
-deprecated to improve user privacy. For backwards
-compatibility, it will still return a small subset of its data including the
-calling application’s own tasks and possibly some other non-sensitive tasks
-such as home. If your app is using this method to retrieve its own tasks,
-use {@code android.app.ActivityManager.getAppTasks()} instead to retrieve that
-information.</p>
+<p>With the introduction of the new <em>concurrent documents and activities
+tasks</em> feature in the upcoming release (see <a href="#Recents">Concurrent
+documents and activities in Recents screen</a> below),
+the {@link android.app.ActivityManager#getRecentTasks
+ActivityManager.getRecentTasks()} method is now deprecated to improve user
+privacy. For backward compatibility, this method still returns a small subset of
+its data, including the calling application’s own tasks and possibly some other
+non-sensitive tasks (such as Home). If your app is using this method to retrieve
+its own tasks, use {@code android.app.ActivityManager.getAppTasks()} instead to
+retrieve that information.</p>
 
 <h2 id="UI">User Interface</h2>
 
 <h3 id="MaterialDesign">Material design support</h3>
 
-<p>The L Developer Preview adds support for the material design style. You can create
-material design apps that are visually dynamic and have UI element transitions
-which feel natural and delightful to users. This support includes:</p>
+<p>The upcoming release adds support for Android's new <em>material</em> design
+style. You can create apps with material design that are visually dynamic and
+have UI element transitions that feel natural to users. This support includes:</p>
+
 <ul>
-  <li>The Material theme</li>
+
+  <li>The material theme</li>
   <li>View shadows</li>
   <li>The {@code RecyclerView} widget</li>
   <li>Drawable animation and styling effects</li>
@@ -182,41 +264,50 @@
   <li>Animators for view properties based on the state of a view</li>
   <li>Customizable UI widgets and app bars with color palettes that you control</li>
 </ul>
+
 <p>To learn more about adding material design functionality to your app, see
-<a href="{@docRoot}preview/material/index.html">Material design on Android</a>.</p>
+<a href="{@docRoot}preview/material/index.html">Material Design</a>.</p>
 
 <h3 id="LockscreenNotifications">Lockscreen notifications</h3>
-<p>Lockscreens in the L Developer Preview have the ability to present notifications.
-Users can choose via <em>Settings</em> whether to allow sensitive notification
-content to be shown over a secure lockscreen.</p>
+<p>Lockscreens in the L Developer Preview have the ability to present
+notifications. Users can choose via <em>Settings</em> whether to allow
+sensitive notification content to be shown over a secure lockscreen.</p>
 
 <p>Your app can control the level of detail visible when its notifications are
 displayed over the secure lockscreen. To control the visibility level, call
 {@code android.app.Notification.Builder.setVisibility()} and specify one of these
 values:</p>
+
 <ul>
 <li>{@code VISIBILITY_PRIVATE}. Shows basic information, such as the
-notification’s icon, but hides the notification’s full content. If you want to
-provide a redacted public version of your notification for the system to display
-on a secure lockscreen, set the public notification object in the <code>publicVersion</code>
-field.</li>
-<li>{@code VISIBILITY_PUBLIC}. Shows the notification’s full content. This is
-  the system default if visibility is left unspecified.</li>
-<li>{@code VISIBILITY_SECRET}. Shows only the most minimal information,
-excluding even the notification’s icon.</li>
+notification’s icon, but hides the notification’s full content.</li>
+<li>{@code VISIBILITY_PUBLIC}. Shows the notification’s full content.</li>
+<li>{@code VISIBILITY_SECRET}. Shows nothing, excluding even the
+notification’s icon.</li>
 </ul>
 
+<p>When {@code VISIBILITY_PRIVATE} is set, you can also provide a redacted
+version of the notification content that hides personal details. For example,
+an SMS app might display a notification that shows "You have 3 new text messages."
+but hides the message content and senders. To provide this alternative
+notification, first create the replacement notification using
+{@link android.app.Notification.Builder}. When you create the private
+notification object, attach the replacement notification to it through the
+{@code Notification.Builder.setPublicVersion()} method.</p>
+
 <h3 id="NotificationsMetadata">Notifications metadata</h3>
 <p>The L Developer Preview uses metadata associated with your app notifications
-to more intelligently sort your notifications. The metadata you set also
+to sort the notifications more intelligently. The metadata you set also
 controls how the system presents your app notifications when the user is in <em>Do
-not disturb</em> mode. When constructing your notification, you can call the
-following methods in {@code android.app.Notification.Builder}:</p>
+Not Disturb</em> mode. To set the metadata, call the following methods in
+{@code android.app.Notification.Builder} when you construct the
+notification:</p>
 
 <ul>
-<li>{@code setCategory()}. Allows the system to handle your app notifications
-in <em>Do not disturb mode</em> (for example, if your notification represents an
-incoming call, instant message, or alarm).</li>
+<li>{@code setCategory()}. Depending on the message category, this tells
+the system how to handle your app notifications when the device is
+in <em>Do not Disturb</em> mode (for example, if your notification represents an
+incoming call, instant message, or alarm).
 <li>{@code setPriority()}. Notifications with the priority field set to
 {@code PRIORITY_MAX} or {@code PRIORITY_HIGH} will appear in a small floating
 window if the notification also has sound or vibration.</li>
@@ -231,30 +322,36 @@
 <p>In previous releases, the
 <a href="{@docRoot}design/get-started/ui-overview.html">Recents screen</a>
 could only display a single task for each app that the user interacted with
-most recently. The L Developer Preview allows your app to open additional tasks
-for concurrent activities or documents. This feature facilitates multitasking
+most recently. The L Developer Preview enables your app to open more tasks as
+needed for additional concurrent activities for documents.
+This feature facilitates multitasking
 by letting users quickly switch between individual activities and documents
-from the Recents screen. Examples of such concurrent tasks might include web
-pages in a browser app, documents in a productivity app, concurrent matches in
+from the Recents screen, with a consistent switching experience across all apps.
+Examples of such concurrent tasks might include open tabs in a web
+browser app, documents in a productivity app, concurrent matches in
 a game, or chats in a messaging app. Your app can manage its tasks
 through the {@code android.app.ActivityManager.AppTask} class.</p>
 
 <p>To insert a logical break so that the system treats your activity as a new
-document, use {@code android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT} when
-launching the activity with {@link android.app.Activity#startActivity(android.content.Intent) startActivity()}. You can also get this behavior by declaring the
+task, use {@code android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT} when
+launching the activity with {@link android.app.Activity#startActivity(android.content.Intent)
+startActivity()}. You can also get this behavior by declaring the
 <a href="{@docRoot}guide/topics/manifest/activity-element.html">&lt;activity&gt;</a>
 attribute {@code documentLaunchMode="intoExisting"} or {@code ="always"} in your
 manifest.</p>
 
 <p>You can also mark that a task should be removed from the Recents screen
-when all its activities are closed by using {@code android.content.Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} when starting the root activity for
+when all its activities are closed. To do this, use {@code
+android.content.Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS} when starting the
+root activity for
 the task. You can also set this behavior for an activity by declaring the
 <a href="{@docRoot}guide/topics/manifest/activity-element.html">&lt;activity&gt;</a>
 attribute {@code autoRemoveFromRecents=“true”} in your manifest.</p>
 
 <p>To avoid cluttering the Recents screen, you can set the maximum number of
-tasks from your app that can appear in the Recents screen through the
-<a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a> attribute {@code android:maxRecent}. The current maximum that can be specified
+tasks from your app that can appear in that screen. To do this, set the
+<a href="{@docRoot}guide/topics/manifest/application-element.html">&lt;application&gt;</a>
+attribute {@code android:maxRecent}. The current maximum that can be specified
 is 100 tasks per user.</a></p>
 
 <h3 id="WebView">WebView updates</h3>
@@ -265,7 +362,8 @@
 been updated to incorporate 36.0.0.0 as the version number.</p>
 
 <p>Additionally, this release brings support for the
-<a href="https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html">WebAudio</a>, <a href="https://www.khronos.org/webgl/">WebGL</a>, and
+<a href="https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html">WebAudio</a>,
+<a href="https://www.khronos.org/webgl/">WebGL</a>, and
 <a href="http://www.webrtc.org/">WebRTC</a> open standards. To learn more about
 the new features included in this release, see <a href="https://developer.chrome.com/multidevice/webview/overview">WebView for Android</a>.</p>
 
@@ -274,16 +372,20 @@
 <h3 id="IME">IME bug fixes and improvements</h3>
 
 <p>Beginning in the L Developer Preview, users can more easily switch between
-all input method editors (IME) <a href="{@docRoot}guide/topics/text/creating-input-method.html">supported by the platform</a>. Performing the designated
+all <a href="{@docRoot}guide/topics/text/creating-input-method.html">input
+method editors (IME)</a> supported by the platform. Performing the designated
 switching action (usually touching a Globe icon on the soft keyboard) will cycle
 among all such IMEs. This change takes place in
-{@code android.view.inputmethod.InputMethodManager.shouldOfferSwitchingToNextInputMethod()}.</p>
+{@link android.view.inputmethod.InputMethodManager#shouldOfferSwitchingToNextInputMethod
+InputMethodManager.shouldOfferSwitchingToNextInputMethod()}.</p>
 
-<p>In addition, the framework will now check whether the next IME includes a
-switching mechanism at all, thus supporting switching to the IME after it. An
+<p>In addition, the framework now checks whether the next IME includes a
+switching mechanism at all (and, thus, whether that IME supports switching to
+the IME after it). An
 IME with a switching mechanism will not cycle to an IME without one. This
 change takes place in
-{@code android.view.inputmethod.InputMethodManager.switchToNextInputMethod()}.
+{@link android.view.inputmethod.InputMethodManager#switchToNextInputMethod
+InputMethodManager.switchToNextInputMethod}.
 
 <p>To see an example of how to use the updated IME-switching APIs, refer to the
 updated soft-keyboard implementation sample in this release.</p>
@@ -298,14 +400,16 @@
 <li>Compute shaders
 <li>Separate shader objects
 <li>Indirect draw commands
-<li>Enhanced texturing functionality
+<li>Multisample and stencil textures
 <li>Shading language improvements
-<li>Optional extensions for per-sample shading, advanced blending modes, and more
+<li>Extensions for advanced blend modes and debugging
 <li>Backward compatibility with OpenGL ES 2.0 and 3.0
 </ul>
 
-<p>The Java interface for OpenGL ES 3.1 on Android is provided with GLES31. When using OpenGL ES 3.1, be sure that you declare it in your manifest file with the
-<a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code &lt;uses-feature&gt;}</a> tag and the {@code android:glEsVversion} attribute. For example:</p>
+<p>The Java interface for OpenGL ES 3.1 on Android is provided with {@code GLES31}. When
+using OpenGL ES 3.1, be sure that you declare it in your manifest file with the
+<a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code &lt;uses-feature&gt;}</a>
+tag and the {@code android:glEsVversion} attribute. For example:</p>
 
 <pre>
 &lt;manifest&gt;
@@ -314,17 +418,45 @@
 &lt;/manifest&gt;
 </pre>
 
-<p>For more information about using OpenGL ES, including how to check the device’s supported OpenGL ES version at runtime, see the <a href="{@docRoot}/guide/topics/graphics/opengl.html">OpenGL ES API guide</a>.</p>
+<p>For more information about using OpenGL ES, including how to check the
+device’s supported OpenGL ES version at runtime, see the
+<a href="{@docRoot}guide/topics/graphics/opengl.html">OpenGL ES API guide</a>.</p>
+
+<h3 id="AndroidExtensionPack">Android Extension Pack</h3>
+
+<p>In addition to OpenGL ES 3.1, this release provides an extension pack with Java interfaces and
+native support for advanced graphics functionality. These extensions are treated as a single
+package by Android. (If the {@code ANDROID_extension_pack_es31} extension is present, your app can
+assume all extensions in the package are present and enable the shading language features with
+a single {@code #extension} statement.</p>
+<p>The extension pack supports:</p>
+<ul>
+<li>Guaranteed fragment shader support for shader storage buffers, images, and
+  atomics (fragment shader support is optional in OpenGL ES 3.1.)</li>
+<li>Tessellation and geometry shaders</li>
+<li>ASTC (LDR) texture compression format</li>
+<li>Per-sample interpolation and shading</li>
+<li>Different blend modes for each color attachment in a frame buffer</li>
+</ul>
+
+<p>The Java interface for the extension pack is provided with {@code GLES31Ext}.
+In your app manifest, you can declare that support for the extension pack is
+required, with the
+<a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code &lt;uses-feature&gt;}</a>
+tag, but the precise syntax is not finalized in the L Developer Preview.</p>
 
 <h2 id="Multimedia">Multimedia</h2>
 
-<h3 id="Camera=v2">Camera v2 API</h3>
+<h3 id="Camera-v2">Camera API for advanced camera capabilities</h3>
 
 <p>The L Developer Preview introduces the new {@code android.hardware.camera2}
-API to facilitate fine grain photo capture and image processing. You can now programmatically access the camera devices available to the system with {@code CameraManager.getCameraIdList()} and connect to a specific device with {@code CameraManager.openCamera()}. To start capturing images, you
-need to create a {@code CameraCaptureSession} and specify the
-{@link android.view.Surface} objects to send the captured images. The {@code CameraCaptureSession} can be configured to take single shots or multiple images
-in a burst.</p>
+API to facilitate fine-grain photo capture and image processing. You can now
+programmatically access the camera devices available to the system with {@code
+CameraManager.getCameraIdList()} and connect to a specific device with {@code
+CameraManager.openCamera()}. To start capturing images, create a {@code
+CameraCaptureSession} and specify the {@link android.view.Surface} objects for
+the captured images. The {@code CameraCaptureSession} can be configured to take
+single shots or multiple images in a burst.</p>
 
 <p>To be notified when new images are captured, implement the
 {@code CameraCaptureSession.CaptureListener()} interface and set it in your
@@ -333,17 +465,22 @@
 {@code onCaptureCompleted()}, providing you with the image capture metadata in a
 {@code CaptureResult}.</p>
 
+<p>To see an example of how to use the updated Camera API, refer to the {@code Camera2Basic}
+and {@code Camera2Video} implementation samples in this release.</p>
+
 <h3 id="AudioPlayback">Audio playback</h3>
-<p>This release includes the following changes for
-  {@code android.media.AudioTrack}:</p>
+<p>This release includes the following changes to
+  {@link android.media.AudioTrack}:</p>
 <ul>
   <li>Your app can now supply audio data in floating-point format
 ({@code android.media.AudioFormat.ENCODING_PCM_FLOAT}). This permits greater
-dynamic range, more consistent precision, and greater headroom. Floating-point arithmetic is especially useful during intermediate calculations. Playback
-end-points use integer format for audio data, and with lower bit-depth. In L
-Developer Preview, portions of the internal pipeline are not yet floating-point.
-  <li>Your app can now supply audio data as a {@code ByteBuffer}, in the same
-format as provided by {@code MediaCodec}.
+dynamic range, more consistent precision, and greater headroom. Floating-point
+arithmetic is especially useful during intermediate calculations. Playback
+end-points use integer format for audio data, and with lower bit-depth. (In the
+L Developer Preview, portions of the internal pipeline are not yet
+floating-point.)
+  <li>Your app can now supply audio data as a {@link java.nio.ByteBuffer}, in
+the same format as provided by {@link android.media.MediaCodec}.
   <li>The {@code WRITE_NON_BLOCKING} option can simplify buffering and
     multithreading for some apps.
 </ul>
@@ -352,8 +489,8 @@
 <p>You can now build your own media controller app with the new
 {@code android.media.session.MediaController} class, which provides
 simplified transport controls APIs that replace those in
-{@code android.media.RemoteControlClient}. The {@code MediaController} class
-allows thread-safe control of playback from a non UI process, making it easier
+{@link android.media.RemoteControlClient}. The {@code MediaController} class
+allows thread-safe control of playback from a non-UI process, making it easier
 to control your media playback service from your app’s user interface.
 
 <p>You can also create multiple controllers to send playback commands,
@@ -362,83 +499,115 @@
 call {@code MediaSession.getSessionToken()} to request an access
 token in order for your app to interact with the session.</p>
 
-<p>Send transport commands such as "play", "stop", "skip", and
+<p>You can now send transport commands such as "play", "stop", "skip", and
 "set rating" by using {@code MediaController.TransportControls}. To handle
-in-bound media transport commands from controllers attached to the session, you
-should override the callback methods in
+in-bound media transport commands from controllers attached to the session,
+override the callback methods in
 {@code MediaSession.TransportControlsCallback}.</p>
 
 <p>You can also create rich notifications that allow playback control tied to a
-media session with the new {@code android.app.Notification.MediaStyle} class.</p>
+media session with the new {@code android.app.Notification.MediaStyle} class. By
+using the new notification and media APIs, you will ensure that the System UI
+knows about your playback and can extract and show album art.</p>
 
 <h2 id="Storage">Storage</h2>
 
 <h3 id="DirectorySelection">Directory selection</h3>
 
-<p>The L Developer Preview extends the <a href="{@docRoot}guide/topics/providers/document-provider.html">Storage Access Framework</a> to let users
-select an entire directory, rather than individual files, to give your app
-read/write access to media files. When a directory is selected, your app also
-has access to all its child directories and content.</p>
+<p>The L Developer Preview extends the <a href="{@docRoot}guide/topics/providers/document-provider.html">Storage Access Framework</a> to let users select an entire directory subtree,
+giving apps read/write access to all contained documents without requiring user
+confirmation for each item.</p>
 
-<p>To get the absolute paths to directories on external storage devices where
-applications can store media files, call the
-{@code android.content.Context.getExternalMediaDirs()} method. No additional
-permissions are needed by your app to read or write to the returned paths.
-External storage devices here are those considered by the system to be a
-permanent part of the device, and includes emulated external storage and
-physical media slots such as SD cards in battery compartments.</p>
+<p>To select a directory subtree, build and send an
+{@code android.intent.action.OPEN_DOCUMENT_TREE} {@link android.content.Intent}.
+The system displays all
+{@link android.provider.DocumentsProvider} instances that support subtree selection,
+letting the user browse and select a directory. The returned URI represents access to the selected
+subtree. You can then use {@code DocumentsContract.buildChildDocumentsUriUsingTree()}
+and {@code DocumentsContract.buildDocumentUriUsingTree()} along with
+{@code ContentResolver.query()} to explore the subtree.</p>
 
-<p>If you want to access a document in an existing directory, call the
-{@code android.provider.DocumentsContract.buildDocumentViaUri()} method and pass
-in a Uri representing the path to the parent directory and the target document
-ID. The method returns a new {@link android.net.Uri} with which your app can
-use to write media content with {@code DocumentsContract.createDocument()}.
+<p>The new {@code DocumentsContract.createDocument()} method lets you create
+new documents or directories anywhere under the subtree. To manage
+existing documents, use {@code DocumentsContract.renameDocument()} and
+{@code DocumentsContract.deleteDocument()}. Check {@code DocumentsContract.Document.COLUMN_FLAGS}
+to verify provider support for these calls before issuing them.</p>
+
+<p>If you're implementing a {@link android.provider.DocumentsProvider} and want
+to support subtree selection, implement {@code DocumentsProvider.isChildDocument()}
+and include {@code Documents.Contract.FLAG_SUPPORTS_IS_CHILD} in your
+{@code Root.COLUMN_FLAGS}.</p>
+
+<p>The L Developer Preview also introduces new package-specific directories on
+shared storage where your app can place media files for inclusion in
+{@link android.provider.MediaStore}. The new
+{@code android.content.Context.getExternalMediaDirs()} returns paths to these
+directories on all shared storage devices. Similarly to
+{@link android.content.Context#getExternalFilesDir(java.lang.String) Context.getExternalFilesDir()},
+no additional permissions are needed by your app to access the returned paths. The
+platform periodically scans for new media in these directories, but you can also
+use {@link android.media.MediaScannerConnection} to explicitly scan for new
+content.</p>
 
 <h2 id="Wireless">Wireless &amp; Connectivity</h2>
 
-<h3 id="Multinetwork">Dynamic network selection and seamless handoff</h3>
-<p>The L Developer Preview provides new multi-networking APIs for your app to
+<h3 id="Multinetwork">Multiple network connections</h3>
+<p>The L Developer Preview provides new multi-networking APIs. These let your app
 dynamically scan for available networks with specific capabilities, and
 establish a connection to them. This is useful when your app requires a
 specialized network, such as an SUPL, MMS, or carrier-billing network, or if
 you want to send data using a particular type of transport protocol.</p>
 
-<p>To select and connect to a network dynamically from your app, first
-instantiate a {@code android.net.ConnectivityManager}. Next, create a
-{@code android.net.NetworkRequest} to specify the network features and transport
-type your app is interested in. To start scanning for suitable networks, call
-{@code ConnectivityManager.requestNetwork()} or
-{@code ConnectivityManager.registerNetworkCallback(), and pass in the
-{@code NetworkRequest} object and an implementation of
-{@code ConnectivityManager.NetworkCallbackListener}.</p>
+<p>To select and connect to a network dynamically from your app follow these
+steps:</p>
+
+<ol>
+ <li>Create a {@link android.net.ConnectivityManager}.</li>
+ <li>Create a
+  {@code android.net.NetworkRequest} to specify the network features and transport
+  type your app is interested in.</li>
+  <li>To scan for suitable networks, call
+  {@code ConnectivityManager.requestNetwork()} or
+  {@code ConnectivityManager.registerNetworkCallback()}, and pass in the
+  {@code NetworkRequest} object and an implementation of
+  {@code ConnectivityManager.NetworkCallbackListener}.</li>
+
+</ol>
 
 <p>When the system detects a suitable network, it connects to the network and
 invokes the {@code NetworkCallbackListener.onAvailable()} callback. You can use
 the {@code android.net.Network} object from the callback to get additional
-information about the network, or to establish a socket connection.</p>
+information about the network, or to direct traffic to use the selected
+network.</p>
 
 <h3 id="BluetoothBroadcasting">Bluetooth broadcasting</h3>
-<p>Android 4.3 introduced platform support for <a href="{@docRoot}guide/topics/connectivity/bluetooth-le.html">Bluetooth Low Energy</a>
+<p>Android 4.3 introduced platform support for
+  <a href="{@docRoot}guide/topics/connectivity/bluetooth-le.html">Bluetooth Low Energy</a>
 (BLE) in the central role. In the L Developer Preview, an Android device can now
-act as a Bluetooth LE <em>peripheral device</em> and make its presence known to
+act as a Bluetooth LE <em>peripheral device</em>. Apps can use this capability
+to make their presence known to
 nearby devices. For instance, you can build apps that allow a device to
 function as a pedometer or health monitor and communicate its data with another
 BLE device.</p>
 
-<p>The new {@code android.bluetooth.le} APIs enable your apps to broadcast advertisements, scan for responses, and form connections with nearby BLE devices.
+<p>The new {@code android.bluetooth.le} APIs enable your apps to broadcast
+advertisements, scan for responses, and form connections with nearby BLE devices.
 You must add the {@code android.permission.BLUETOOTH_ADMIN} permission in your
 manifest in order for your app to use the new advertising and scanning features.</a>
 
-<p>To begin Bluetooth LE advertising so that other devices can discover the
-device running your app, call {@code android.bluetooth.le.BluetoothAdvertiser.startAdvisertising()} and pass in an implementation of the
-{@code android.bluetooth.le.AdvertiseCallback} class to report the success
-or failure of the advertising operation.</p>
+<p>To begin Bluetooth LE advertising so that other devices can discover
+your app, call {@code android.bluetooth.le.BluetoothAdvertiser.startAdvisertising()}
+and pass in an implementation of the
+{@code android.bluetooth.le.AdvertiseCallback} class. The callback object
+receives a report of the success or failure of the advertising operation.</p>
 
-<p>Conversely, if you want to scan for Bluetooth LE devices nearby, call
-{@code android.bluetooth.le.BluetoothLeScanner.startScan()} and pass in an
+<p> The L Developer Preview introduces the {@code
+android.bluetooth.le.ScanFilter} class so that your app can scan for only the
+specific types of devices it is interested in. To begin scanning for Bluetooth
+LE devices, call {@code android.bluetooth.le.BluetoothLeScanner.startScan()} and
+pass in a list of filters. In the method call, you must also provide an
 implementation of {@code android.bluetooth.le.ScanCallback} to report if a
-Bluetooth LE advertisement is found. Optionally, you can pass in filters to scan
-for a specific type of device.</p>
+Bluetooth LE advertisement is found. </p>
 
 <h3 id="NFCEnhancements">NFC enhancements</h3>
 <p>The L Developer Preview adds these enhancements to enable wider and more
@@ -446,13 +615,12 @@
 
 <ul>
 <li>Android Beam is now available in the share menu.
-<li>Support for the <a href="http://www.wi-fi.org/discover-wi-fi/wi-fi-direct">Wi-fi Direct standard</a>.
 <li>Your app can invoke the Android Beam on the user’s device to share data by
 calling {@code android.nfc.NfcAdapter.invokeBeam()}. This avoids the need for
 the user to manually tap the device against another NFC-capable device to
 complete the data transfer.
-<li>Use the new {@code android.nfc.NdefRecord.createTextRecord()} method if
-  you want to create an NDEF record containing UTF-8 text data.
+<li>You can use the new {@code android.nfc.NdefRecord.createTextRecord()} method
+to create an NDEF record containing UTF-8 text data.
 <li>If you are developing a payment app, you now have the ability to
 register an NFC application ID (AID) dynamically by calling
 {@code android.nfc.cardemulation.CardEmulation.registerAidsForService()}.
@@ -466,18 +634,32 @@
 <h3 id="JobScheduler">Scheduling jobs</h3>
 <p>The L Developer Preview provides a new {@code android.app.job.JobScheduler}
 API that lets you optimize battery life by defining jobs for the system to run
-asynchronously at a later time, such as when the device is charging. This is
-useful when you want to defer non user-facing units of work, have application
-code that accesses the network, or want to run a number of tasks as a batch on
-a regular schedule.</p>
+asynchronously at a later time or under specified conditions (such as when the
+device is charging). This is useful in such situations as:</p>
+<ul>
+  <li>The app has non-user-facing work that you can defer.</li>
+  <li>The app has work you'd prefer to do when the unit is plugged in.</li>
+  <li>The app has a task that requires network access (or requires a Wi-Fi
+    connection).</li>
+  <li>The app has a number of tasks that you want to run as a batch on a regular
+   schedule.</li>
 
-<p>A {@code android.app.job.JobInfo} object encapsulates such a unit of work,
-and provides an exact description of the criteria you are scheduling.</p>
+</ul>
+
+<p>A unit of work is encapsulated by a {@code android.app.job.JobInfo} object.
+This object provides an exact description of the criteria to be used for
+scheduling.</p>
 
 <p>Use the {@code android.app.job.JobInfo.Builder} to configure how the
 scheduled task should run. You can schedule the task to run under specific
-conditions such as only while the device is charging, when connected to an
-unmetered network, or when the system deems the device is idle.</p>
+conditions, such as:</p>
+
+<ul>
+  <li>The device is charging</li>
+  <li>The device is connected to an unmetered network</li>
+  <li>The system deems the device to be idle</li>
+  <li>Completion with a minimum delay or within a specific deadline.</li>
+</ul>
 
 <p>For example, you can add code like this to run your task on an
 unmetered network:</p>
@@ -492,7 +674,10 @@
 jobScheduler.schedule(uploadTask);
 </pre>
 
-<h3 id="PowerMeasurementTools">Developer tools and APIs for power measurement</h3>
+<p>To see an example of how to use the {@code JobScheduler} API, refer to the
+{@code JobSchedulerSample} implementation sample in this release.</p>
+
+<h3 id="PowerMeasurementTools">Developer tools for power measurement</h3>
 <p>The L Developer Preview provides several new developer tools and APIs to help
 you better measure and understand your app's power usage.</p>
 
@@ -513,23 +698,33 @@
 </ul>
 
 <p>Use the {@code --help} option to learn about the various options for
-tailoring the output. For example, to run the tool to print battery usage
-statistics since the device was last charged for a given app package, run this
+tailoring the output. For example, to print battery usage
+statistics for a given app package since the device was last charged, run this
 command:
 <pre>
-$ adb shell dumpsys batterystats --charged <package-name>
+$ adb shell dumpsys batterystats --charged &lt;package-name&gt;
 </pre>
 </dd>
 
 <dt><strong>Battery Historian</strong></dt>
 <dd>
-<p>The Battery Historian tool ({@code historian.par}) analyzes L-based Android
-bug reports and creates an HTML visualization of power-related events. It can
-also visualize power consumption data from a power monitor, and will attempt to
-map power usage to the wakelocks seen. You can find the Battery Historian tool
+<p>The Battery Historian tool ({@code historian.par}) analyzes Android
+bug reports from the L Developer Preview and creates an HTML visualization of
+power-related events. It can
+also visualize power consumption data from a power monitor, and attempts to
+map power usage to the wake locks seen. You can find the Battery Historian tool
 in {@code &lt;sdk&gt;/tools}.</p>
 
-<p>For best results, you should first enable full wakelock reporting to allow
+<img src="images/battery_historian.png"
+     srcset="images/battery_historian@2x.png 2x"
+    alt="" width="760" height="462"
+    id="figure2" />
+<p class="img-caption">
+  <strong>Figure 2.</strong>HTML visualization generated by the Battery
+      Historian tool.
+</p>
+
+<p>For best results, you should first enable full wake lock reporting, to allow
 the Battery Historian tool to monitor uninterrupted over an extended period of
 time:</p>
 <pre>
@@ -548,93 +743,118 @@
 </pre>
 </dd>
 
-<dt><strong>On-device power management</strong></dt>
-<dd>
-<p>You can use the {@code android.os.BatteryManager} API to obtain power
-consumption information based on the battery fuel gauge included in Android
-phones and tablets. This is useful in cases when it is not convenient to
-connect external measurement equipment to the Android device.</p>
-<p>To retrieve the battery properties, call {@code BatteryManager.getIntProperty()}
-or {@code BatteryManager.getLongProperty()}. The properties available, the
-exact resolution of the values of each, and other characteristics such as
-update frequency depend on the particular device being tested.</p>
-
-<p>The following properties can be inspected on all Android devices:</p>
-
-<table>
-  <tr>
-     <th>Property</th>
-     <th>Description</th>
-  </tr>
-  <tr>
-     <td>{@code BatteryManager.BATTERY_PROPERTY_CHARGE_COUNTER}</td>
-     <td>Remaining battery capacity in microampere-hours.</td>
-  </tr>
-  <tr>
-     <td>{@code BatteryManager.BATTERY_PROPERTY_CURRENT_NOW}</td>
-     <td>Instantaneous battery current in microamperes.</td>
-  </tr>
-  <tr>
-     <td>{@code BatteryManager.BATTERY_PROPERTY_CURRENT_AVERAGE}</td>
-     <td>Average battery current in microamperes</td>
-  </tr>
-  <tr>
-     <td>{@code BatteryManager.BATTERY_PROPERTY_CAPACITY}</td>
-     <td>Remaining battery capacity as an integer percentage.</td>
-  </tr>
-  <tr>
-     <td>{@code BatteryManager.BATTERY_PROPERTY_ENERGY_COUNTER}</td>
-     <td>Remaining energy in nanowatt-hours.</td>
-  </tr>
-</table>
-<dd>
 </dl>
 
 <h2 id="Enterprise">Enterprise</h2>
 <h3 id="ManagedProvisioning">Managed provisioning</h3>
 
+<div class="figure" style="width:360px">
+  <img src="images/managed_apps_launcher.png"
+    srcset="images/managed_apps_launcher@2x.png 2x"
+    alt="" width="360" height="609" id="figure3" />
+  <p class="img-caption">
+    <strong>Figure 3.</strong> Launcher screen showing managed apps (marked with
+    a lock badge)
+  </p>
+</div>
+
 <p>The L Developer Preview provides new functionality for running apps within
-an enterprise environment:</p>
+an enterprise environment. A
+<a href="{@docRoot}guide/topics/admin/device-admin.html">device administrator</a> can
+initiate a managed provisioning process to add a co-present but separate <em>Android
+work profile</em> to a device, if the user has an existing personal account.
+Apps that are associated with Android work profiles will appear alongside
+non-managed apps in the user’s Launcher, Recent apps screen, and notifications.</p>
+
+<p>To start the managed provisioning process, send {@code
+ACTION_PROVISION_MANAGED_PROFILE} in an {@link android.content.Intent}. If the
+call is successful, the system triggers the {@code
+android.app.admin.DeviceAdminReceiver. onProfileProvisioningComplete()} callback.
+You can then call {@code app.admin.DevicePolicyManager. setProfileEnabled()} to
+enable this Android work profile.</p>
+
+<p>A device administrator can associate a user with more than one Android
+work profile. To get a list of the Android work profiles associated with the
+user, call {@code android.os.UserManager. getUserProfiles()}.</p>
+
+<p>If you are developing a Launcher app, you can use the new {@code
+android.content.pm.LauncherApps} class to get a list of launchable activities
+for the current user and any associated Android work profiles. Your Launcher can make
+the managed apps visually prominent by appending a “work” badge to the icon
+drawable with {@code android.os.UserManager. getBadgeDrawableForUser()}.</p>
+
+<p>To see an example of how to use the new functionality, refer to the
+{@code BasicManagedProfile} implementation sample in this release.</p>
+
+<h3 id="TaskLocking">Task locking</h3>
+<p>The L Developer Preview introduces a new task locking API that
+lets you temporarily restrict users from leaving your app or being interrupted
+by notifications. This could be used, for example, if you are developing an
+education app to support high stakes assessment requirements on Android.
+Once your app activates this mode, users will not be able to see
+notifications, access other apps, or return to the Home screen, until your
+app exits the mode.</p>
+
+<p>To prevent unauthorized usage, only authorized apps can activate task locking.
+Furthermore, task locking authorization must be granted by a
+specially-configured <em>device owner</em> app, through the {@code android.app.admin.DevicePolicyManager.setLockTaskComponents()} method.</p>
+
+<p>To set up a device owner, follow these steps:</p>
+<ol>
+<li>Attach a device running an <a href="https://source.android.com/source/building-running.html">Android {@code userdebug} build</a> to your development machine.</li>
+<li>Install your device owner app.</li>
+<li>Create a {@code device_owner.xml} file and save it to the {@code /data/system}
+directory on the device.
+<pre>
+$ adb root
+$ adb shell stop
+$ rm /tmp/device_owner.xml
+$ echo "&lt;?xml version='1.0' encoding='utf-8' standalone='yes' ?&gt;"
+&gt;&gt; /tmp/device_owner.xml
+$ echo "&device-owner package=\"&lt;your_device_owner_package&gt;\"
+name=\"*&lt;your_organization_name&gt;\" /&gt;" &gt;&gt; /tmp/device_owner.xml
+$ adb push /tmp/device_owner.xml /data/system/device_owner.xml
+$ adb reboot
+</pre>
+</li>
+</ol>
+
+<p>Before using the task locking API in your app, verify that your activity is
+authorized by calling {@code DevicePolicyManager.isLockTaskPermitted()}.</p>
+
+<p>To activate task locking, call
+{@code android.app.Activity.startLockTask()} from your authorized activity.</p>
+
+<p>When task locking is active, the following behavior takes effect:</p>
+
 <ul>
-<li><strong>Create managed user profiles</strong>. A device administrator can
-initiate a managed provisioning process to enroll a user device with an
-existing personal account into a co-present but separate managed profile that
-the administrator controls.
-<li><strong>Set device owner scope</strong>. Device administrators can also
-apply managed provisioning to configure a device that has no previous user
-accounts installed, so that they have full control over the device.
+<li>The status bar is blank, and user notifications and status information is
+hidden.</li>
+<li>The Home and Recent Apps buttons are hidden.</li>
+<li>Other apps may not launch new activities.</li>
+<li>The current app may start new activities, as long as doing so does not
+create new tasks.</li>
+<li>The user remains locked on your app until an authorized activity calls
+{@code Activity.stopLockTask()}.</li>
 </ul>
 
-<p>To start the manged provisioning process, send
-{@code ACTION_PROVISION_MANAGED_PROFILE} in an {@link android.content.Intent}. A
-user may be associated with more than one managed profile. To get a list of the
-managed profiles associated with the user, call
-{@code android.os.UserManager.getUserProfiles()}.</p>
-
-<p>Once a managed profile is created for a user, apps that are managed by the
-device administrator will appear alongside non-managed apps in the user’s
-Launcher, Recent apps screen, and notifications. A device policy management app
-can make the managed apps visually prominent by appending a “work” badge to the
-icon drawable with {@code android.os.UserManager.getBadgeDrawableForUser()}.</p>
-
-<p>If you are developing a Launcher app, you can use the new {@code android.content.pm.LauncherApps} class to get a list of launchable activities for the current user
-and any associated managed profiles.</p>
-
 <h2 id="Printing">Printing Framework</h2>
 
 <h3 id="PDFRender">Render PDF as bitmap</h3>
 <p>You can now render PDF document pages into bitmap images for printing by
 using the new {@code android.graphics.pdf.PdfRenderer} class. You must specify a
-{@code ParcelFileDescriptor} that is seekable (that is, the file can be randomly
-accessed) on which the system writes the the printable content. Your app can
-obtain a page for rendering with {@code openPage()}, then call {@code render()}
-to turn the opened {@code PdfRenderer.Page} into a bitmap. You can also set
-additional parameters if you only wan to convert a portion of the document into
-a bitmap image (for example, to implement <a href="http://en.wikipedia.org/wiki/Tiled_rendering">tile rendering</a> in order to zoom in on the document).</p>
+{@link android.os.ParcelFileDescriptor} that is seekable (that is, the content
+can be randomly accessed) on which the system writes the the printable content.
+Your app can obtain a page for rendering with {@code openPage()}, then call
+{@code render()} to turn the opened {@code PdfRenderer.Page} into a bitmap. You
+can also set additional parameters if you only want to convert a portion of the
+document into a bitmap image (for example, to implement
+<a href="http://en.wikipedia.org/wiki/Tiled_rendering">tiled rendering</a> in
+order to zoom in on the document).</p>
 
 <h2 id="TestingA11y">Testing &amp; Accessibility </h2>
 
-<h3 id="Testing A11yImprovements">Testing and accessibility improvements</h3>
+<h3 id="TestingA11yImprovements">Testing and accessibility improvements</h3>
 <p>The L Developer Preview adds the following support for testing and
 accessibility:</p>
 
@@ -644,55 +864,51 @@
 capture frame statistics for window animations and content. This lets you
 write instrumentation tests to evaluate if the app under test is rendering
 frames at a sufficient refresh frequency to provide a smooth user experience.
+
 <li>You can execute shell commands from your instrumentation test with the new
 {@code android.app.UiAutomation.executeShellCommand()}. The command execution
-is similar to running 'adb shell' from a host connected to the device. This
+is similar to running {@code adb shell} from a host connected to the device. This
 allows you to use shell based tools such as {@code dumpsys}, {@code am},
 {@code content}, and {@code pm}.
+
 <li>Accessibility services and test tools that use the accessibility APIs
-(such as <a href="{@docRoot}tools/help/uiautomator/index.html">UiAutomator</a>)
+(such as <a href="{@docRoot}tools/help/uiautomator/index.html">uiautomator</a>)
 can now retrieve detailed information about the properties of windows on the
 screen that sighted users can interact with. To retrieve a list of
-{@code android.view.accessibility.AccessibilityWindowInfo} representing the
-windows information, call the new
+{@code android.view.accessibility.AccessibilityWindowInfo} objects
+representing the windows information, call the new
 {@code android.accessibilityservice.AccessibilityService.getWindows()} method.
 <li>You can use the new {@code android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction} to define standard or customized
-actions to perform on an {@code android.view.accessibility.AccessibilityNodeInfo}.
+actions to perform on an {@link android.view.accessibility.AccessibilityNodeInfo}.
 The new {@code AccessibilityAction} class replaces the actions-related APIs
 previously found in {@code AccessibilityNodeInfo}.
 </ul>
 
-<h2 id="manifest">Manifest Declarations</h2>
+<h2 id="Manifest">Manifest Declarations</h2>
 
 <h3 id="ManifestFeatures">Declarable required features</h3>
-<p>The following values are now supported in the <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code &lt;uses-feature&gt;}</a> element so you
-can ensure that your app is installed only on devices that provide the features
+<p>The following values are now supported in the
+<a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code &lt;uses-feature&gt;}</a>
+element, so you can ensure that your app is installed only on devices that provide the features
 your app needs.</p>
 
 <ul>
-<li>{@code FEATURE_LEANBACK}. Declares that your app must be installed only on devices that support the <a href="{@docRoot}tv}">Android TV</a> user interface. Example:
+<li>{@code FEATURE_LEANBACK}. Declares that your app must be installed only on
+devices that support the
+<a href="{@docRoot}training/tv/index.html">Android TV</a> user interface.
+Example:
 <pre>
 &lt;uses-feature android:name="android.software.leanback"
               android:required="true" /&gt;
 </pre>
 
-<li>{@code FEATURE_MANAGEDPROFILES}. Declares that your app must only be installed on devices that support managed profiles for enterprise users. Example:
-<pre>
-&lt;uses-feature android:name="android.software.managedprofiles"
-              android:required="true" /&gt;
-</pre>
-<li>{@code FEATURE_WEBVIEW}. Declares that your app must only be installed on devices that fully implement the android.webkit.* APIs. Example:
+<li>{@code FEATURE_WEBVIEW}. Declares that your app must only be installed on
+devices that fully implement the {@code android.webkit.*} APIs. Example:
 <pre>
 &lt;uses-feature android:name="android.software.webview"
               android:required="true" /&gt;
 </pre>
 </ul>
 
-<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&gt;}</a> to declare the
-permissions your app requires in order to access certain APIs.
-
-<ul>
-<li>{@code SIM_COMMUNICATION}. Required to communicate with a SIM card using
-  logical channels.
-</ul>
+<p class="note">For a detailed view of all API changes in the L Developer Preview, see the
+<a href="{@docRoot}preview/reference.html">API Differences Report</a>.</p>
diff --git a/docs/html/preview/google-play-services-wear.html b/docs/html/preview/google-play-services-wear.html
new file mode 100644
index 0000000..ad8891f
--- /dev/null
+++ b/docs/html/preview/google-play-services-wear.html
@@ -0,0 +1,95 @@
+<html><head>
+
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta name="viewport" content="width=device-width">
+
+<meta name="Description" content="Google Play services will fully roll out to the hundreds of millions of Android devices in early July. Because of this, we usually wait to release the Google Play services SDK until all users receive the most updated Google Play services on their devices…">
+<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
+<title>Google Play services Preview for Wear | Android Developers</title>
+
+<!-- STYLESHEETS -->
+<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:light,regular,medium,thin,italic,mediumitalic,bold" title="roboto">
+<link href="/assets/css/default.css" rel="stylesheet" type="text/css">
+</head>
+<body>
+<div class="col-12" id="doc-col">
+
+<h1 itemprop="name">Google Play services Preview for Wear</h1>
+      
+    
+  
+
+
+  
+  <div id="jd-content">
+  <div class="jd-descr" itemprop="articleBody">
+<p>The Google Play services app is currently being rolled out to the hundreds of millions of
+Android devices and will complete in early July. Because of this, we usually wait to release the Google Play
+services SDK until all users receive the app. This guarantees that your newly-updated apps can
+run on the most devices as possible.</p>
+
+<p>However, if want to develop for Android Wear now, complete the following steps
+to get special access to all the things you need to start developing, without waiting
+for the rollout to complete. </p>
+
+<p class="warning"><b>Warning</b>: Do not publish any apps that use the new Google Play services
+APIs until the rollout is complete. Your apps will break on most user devices, which will
+degrade your user rating.</b>
+</p>
+
+<h2 style="margin-bottom: 0px;">1. Get Whitelisted for the Preview</h2><hr>
+
+<p>If you attended Google I/O, your registered Gmail account is automatically whitelisted for these
+preview resources. If you didn't attend Google I/O, sign up below to get access:</p>
+
+<a href="https://groups.google.com/forum/?hl=en#!forum/io14androidweardev">Get Whitelisted</a>
+
+<h2 style="margin-bottom: 0px;">2. Download Required Apps</h2><hr>
+<p>You'll need the following apps to get the most out of Android Wear:</p>
+
+
+<p>Here's a list of the apps you need:</p>
+<ul>
+<li><a href="https://play.google.com/apps/testing/com.google.android.gms">Google Play services</a>: Allows your Wear device to properly communicate with your handheld device. This is
+required to use the Android Wear Companion App.</li>
+  <li><a href="https://play.google.com/apps/testing/com.google.android.wearable.app">Android Wear
+  Companion</a>: The main user app to pair a handheld to a wearable and to provide syncing
+  of notifications and data.</li>
+  <li><a href="https://play.google.com/apps/testing/com.google.android.googlequicksearchbox">Google
+  Search</a>: A preview release of the Google Search handheld app that Wear communicates with
+  to carry out searches.</li>
+  <li><a href="https://play.google.com/apps/testing/com.google.android.keep">Google Keep</a>: To enable the "Take a note" command</li>
+  <li><a href="https://play.google.com/apps/testing/com.google.samples.apps.iosched">Google I/O 2014</a></li>
+ </ul>
+
+<p>To enable the preview versions of the apps, click each app link above and follow these
+instructions:</p>
+
+<ol>
+ <li>Click the <b>Become a Tester</b> button to opt-in to the preview version of the app. The page
+ confirms that you're a tester after clicking.</li>
+ <li>Click the <b>Download &lt;app name&gt; from the Play Store</b> link to go to Google Play
+ Store download page to get the app. The
+ following screenshot shows how the opt-in process looks like:
+<img style="margin-top:40px" src="/preview/images/opt-in.png"></li>
+ <li>When Google Play services is rolled out to all devices, go back to the app links provided
+  to opt-out of the preview versions of the apps. Check back here in a week for the status of
+  the rollout.</li>
+</ol>
+
+
+
+<h2 style="margin-bottom: 0px;">3. Start Building</h2><hr>
+<p>Check out the <a href="/training/building-wearables">Building Apps for Wearables</a>
+training classes for information on how to build for Wear.</p>
+    </div>
+
+   
+      
+
+  </div> <!-- end jd-content -->
+</div><!-- end doc-content -->
+</div> <!-- end body-content --> 
+</body>
+</html>
\ No newline at end of file
diff --git a/docs/html/preview/images/android.png b/docs/html/preview/images/android.png
deleted file mode 100644
index 3aeaa98..0000000
--- a/docs/html/preview/images/android.png
+++ /dev/null
Binary files differ
diff --git a/docs/html/preview/images/battery_historian.png b/docs/html/preview/images/battery_historian.png
new file mode 100644
index 0000000..f1d4e40
--- /dev/null
+++ b/docs/html/preview/images/battery_historian.png
Binary files differ
diff --git a/docs/html/preview/images/battery_historian@2x.png b/docs/html/preview/images/battery_historian@2x.png
new file mode 100644
index 0000000..8c8a87f
--- /dev/null
+++ b/docs/html/preview/images/battery_historian@2x.png
Binary files differ
diff --git a/docs/html/preview/images/hun-example.png b/docs/html/preview/images/hun-example.png
new file mode 100644
index 0000000..251b938
--- /dev/null
+++ b/docs/html/preview/images/hun-example.png
Binary files differ
diff --git a/docs/html/preview/images/hun-example@2x.png b/docs/html/preview/images/hun-example@2x.png
new file mode 100644
index 0000000..5b98a361
--- /dev/null
+++ b/docs/html/preview/images/hun-example@2x.png
Binary files differ
diff --git a/docs/html/preview/images/l-dev-prev.png b/docs/html/preview/images/l-dev-prev.png
new file mode 100644
index 0000000..eae6ede
--- /dev/null
+++ b/docs/html/preview/images/l-dev-prev.png
Binary files differ
diff --git a/docs/html/preview/images/managed_apps_launcher.png b/docs/html/preview/images/managed_apps_launcher.png
new file mode 100644
index 0000000..b5ef407
--- /dev/null
+++ b/docs/html/preview/images/managed_apps_launcher.png
Binary files differ
diff --git a/docs/html/preview/images/managed_apps_launcher@2x.png b/docs/html/preview/images/managed_apps_launcher@2x.png
new file mode 100644
index 0000000..90d7d51
--- /dev/null
+++ b/docs/html/preview/images/managed_apps_launcher@2x.png
Binary files differ
diff --git a/docs/html/preview/images/opt-in.png b/docs/html/preview/images/opt-in.png
new file mode 100644
index 0000000..51754af
--- /dev/null
+++ b/docs/html/preview/images/opt-in.png
Binary files differ
diff --git a/docs/html/preview/index.html b/docs/html/preview/index.html
new file mode 100644
index 0000000..4f3f150
--- /dev/null
+++ b/docs/html/preview/index.html
@@ -0,0 +1,361 @@
+<!DOCTYPE html>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<html>
+<head>
+
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta name="viewport" content="width=970" />
+
+<meta name="Description" content="Test and build your apps against the next version of Android to ensure they're ready when the platform officially launches.">
+<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
+<title>Android L Developer Preview | Android Developers</title>
+
+<!-- STYLESHEETS -->
+<link rel="stylesheet"
+href="//fonts.googleapis.com/css?family=Roboto+Condensed">
+<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:light,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 metaTags = [];
+  var devsite = false;
+</script>
+<script src="/assets/js/docs.js" type="text/javascript"></script>
+
+<script>
+  (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
+  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
+  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
+  })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
+
+  ga('create', 'UA-5831155-1', 'android.com');
+  ga('create', 'UA-49880327-2', 'android.com', {'name': 'universal'});  // New tracker);
+  ga('send', 'pageview');
+  ga('universal.send', 'pageview'); // Send page view for new tracker.
+</script>
+
+</head>
+
+<body class="gc-documentation 
+
+" itemscope itemtype="http://schema.org/Article">
+
+  
+<a name="top"></a>
+<div id="body-content">
+<div class="fullpage" >
+<div id="jd-content">
+  <div class="jd-descr" itemprop="articleBody">
+    <style>
+.fullpage>#footer,
+#jd-content>.content-footer.wrap {
+  display:none;
+}
+</style>
+
+<style>
+#footer {
+    display: none;
+}
+.content-footer {
+  display: none;
+}
+</style>
+
+    <div class="landing-rest-of-page">
+      <div class="landing-section" style="padding-top:30px">
+        <div class="wrap">
+          <div class="landing-section-header">
+            <div class="landing-h1">Android L Developer Preview</div>
+            <div class="landing-subhead">
+              Get an early look at the next release and  get your apps ready when the
+              platform officially launches.
+            </div>
+
+            <img src="/preview/images/l-dev-prev.png" style=" margin:0px 0 0 40px" width="860px"/>
+            <div class="col-6" style="margin-left:660px; margin-top:-105px">
+   <a href="/preview/setup-sdk.html" class="landing-button landing-primary" style="position:absolute;z-index:100;float:right;margin-top: 0px;">Get Started</a><!--
+            <p>Set up your environment and check out all the docs to get up and running.</p>-->
+             
+         
+            </div>
+          </div>
+        </div> <!-- end .wrap -->
+      </div> <!-- end .landing-section -->
+
+
+
+<div class="landing-section landing-gray-background" style="margin-top:-135px; padding-bottom:20px">
+        <div class="wrap">
+          <div class="cols">
+<div class="landing-body" style="margin-top:-80px" >
+
+            <div class="landing-breakout cols">
+              <div class="col-4">
+                <p>A New UI Design</p>
+                <p class="landing-small">
+                  Create a consistent experience across mobile and the web with
+                   <b>material design</b>, the new Google-wide standard.
+                </p>
+                <p class="landing-small">
+                  <a href="/preview/material/index.html">Learn about material</a>
+                </p>
+              </div>
+              <div class="col-4">
+                <p>A New Runtime</p>
+                <p class="landing-small">
+                  Test your apps and get them ready for <b>ART</b> (<b>A</b>ndroid <b>R</b>un<b>t</b>ime),
+                  the default runtime in the next release.
+                </p>
+                <p class="landing-small">
+                  <a href="/preview/api-overview.html#ART">Learn about ART</a>
+                </p>
+              </div>
+              <div class="col-4">
+                <p style="width:230px">Enhanced Notifications</p>
+                <p class="landing-small">
+                   Get control over where notifications appear,
+                   how they look, and how they sync to non-handheld devices.
+                </p>
+                <p class="landing-small">
+                  <a href="/preview/api-overview.html#UI">Learn about notifications</a>
+                </p>
+              </div>
+              <div class="col-4">
+                <p>Increased Efficiency</p>
+                <p class="landing-small">
+                  <b>Project Volta</b> is our effort to make the platform energy efficient and
+                  to give you more control over resource usage.
+                </p>
+                <p class="landing-small">
+                  <a href="/preview/api-overview.html#Power">Learn about Project Volta</a>
+                </p>
+              </div>
+            </div>
+               <p style="margin-left:20px">See the <a href="/preview/api-overview.html">API overview</a> for more information
+              on the rest of the new and updated features.</p>
+          </div>
+          </div></div></div>
+    <div class="landing-section">
+        <div class="wrap">
+          <div class="cols">
+            <div class="landing-body">
+              <div class="col-3-wide">
+                  <a target="_blank" href="https://code.google.com/p/android-developer-preview/">
+                    <img class="landing-social-image" src="/preview/images/bugs.png" alt="">
+                  </a>
+                <div class="landing-social-copy">
+                  <p>Issue Tracker</p>
+                  <p class="landing-small">
+                  Let us know when you encounter problems, so we can fix them and make
+                  the platform better for you and your users.
+                    </p><p class="landing-small">
+                      <a href="https://code.google.com/p/android-developer-preview/">
+                      Report Issues</a>
+                    </p>
+                  <p></p>
+                </div>
+              </div>
+              <div class="col-3-wide">
+                <a target="_blank" href="http://plus.google.com">
+                  <img class="landing-social-image" src="//www.google.com/images/icons/product/gplus-128.png" alt="">
+                </a>
+                <div class="landing-social-copy">
+                  <p>Google+ </p>
+                  <p class="landing-small">
+                    Join the community of Android developers testing out the L Developer Preview and
+                    share your thoughts and experiences.
+                  </p><p class="landing-small">
+                    <a href="https://plus.google.com/communities/113159138894928487684">
+                    Discuss on Google+</a>
+                    </p>
+                </div>
+              </div>
+              <div class="col-3-wide">
+                <a target="_blank" href="/preview/support.html">
+                  <img class="landing-social-image" src="/preview/images/updates.png" alt="">
+                </a>
+                <div class="landing-social-copy">
+                  <p>Support and Updates</p>
+                  <p class="landing-small">
+                  Updates to the L Developer Preview are delivered
+                  in the Android SDK Manager. Check back periodically
+                  for news about the changes.
+                  </p>
+                  <p class="landing-small">
+                    <a href="/preview/support.html">Get Support</a>
+                  </p>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+
+    <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
+      <div class="layout-content-col col-16" style="padding-top:4px">
+        <style>#___plusone_0 {float:right !important;}</style>
+        <div class="g-plusone" data-size="medium"></div>
+      </div>
+    </div>
+    <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
+      <div id="copyright">
+        Except as noted, this content is
+        licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
+        Creative Commons Attribution 2.5</a>. For details and
+        restrictions, see the <a href="/license.html">Content
+        License</a>.
+      </div>
+    </div>
+  </div> <!-- end landing-body-content -->
+
+  <script>
+  $("a.landing-down-arrow").on("click", function(e) {
+    $("body").animate({
+      scrollTop: $(".preview-hero").height() + 76
+    }, 1000, "easeOutQuint");
+    e.preventDefault();
+  });
+  </script>
+    </div>
+
+      <div class="content-footer wrap"
+                    itemscope itemtype="http://schema.org/SiteNavigationElement">
+        
+        <div class="paging-links layout-content-col col-10">
+          
+        </div>
+        <div class="layout-content-col plus-container col-2" >
+          <style>#___plusone_0 {float:right !important;}</style>
+            <div class="g-plusone" data-size="medium"></div>
+          
+        </div>
+        
+      </div>
+
+      
+      
+
+  </div> <!-- end jd-content -->
+
+<div id="footer" class="wrap" style="width:940px">
+        
+
+  <div id="copyright">
+    
+  Except as noted, this content is 
+  licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
+  Creative Commons Attribution 2.5</a>. For details and 
+  restrictions, see the <a href="/license.html">Content 
+  License</a>.
+  </div>
+
+
+</div> <!-- end footer -->
+</div><!-- end doc-content -->
+
+</div> <!-- end body-content --> 
+
+
+
+
+
+  <script src="https://developer.android.com/ytblogger_lists_unified.js" type="text/javascript"></script>
+  <script src="/jd_lists_unified.js" type="text/javascript"></script>
+  <script src="/jd_extras.js" type="text/javascript"></script>
+  <script src="/jd_collections.js" type="text/javascript"></script>
+  <script src="/jd_tag_helpers.js" type="text/javascript"></script>
+
+</body>
+</html>
diff --git a/docs/html/preview/index.jd b/docs/html/preview/index.jd
deleted file mode 100644
index e44e9f3..0000000
--- a/docs/html/preview/index.jd
+++ /dev/null
@@ -1,236 +0,0 @@
-page.title=Android L Developer Preview
-page.viewport_width=970
-fullpage=true
-no_footer_links=true
-page.type=about
-page.metaDescription=Test and build your apps against the next version of Android to ensure they're ready when the platform officially launches.
-page.image={@docRoot}preview/images/hero.jpg
-@jd:body
-
-<style>
-.fullpage>#footer,
-#jd-content>.content-footer.wrap {
-  display:none;
-}
-</style>
-
-<style>
-#footer {
-    display: none;
-}
-.content-footer {
-  display: none;
-}
-</style>
-
-<div class="landing-body-content">
-  <div class="landing-hero-container">
-    <div class="landing-section preview-hero">
-      <div class="landing-hero-scrim"></div>
-      <div class="landing-hero-wrap">
-        <div class="vertical-center-outer">
-          <div class="vertical-center-inner">
-
-            <div class="col-12">
-              <div class="landing-section-header">
-
-                <div class="landing-h1 hero">L Developer Preview</div>
-                <div class="landing-subhead hero">
-                <p>An early look at the next release</p>
-                </div>
-              <div class="landing-hero-description">
-               <p>Test and build your apps against the next<br />
-              version of Android to ensure they're ready<br/>
-              when the platform officially launches.</p>
-              </div>
-
-              <div class="landing-body">
-                <a href="/preview/setup-sdk.html" class="landing-button landing-primary" style="margin-top: 40px;">
-                  Get Started
-                </a>
-              </div>
-            </div>
-
-          </div>
-        </div>
-      </div> <!-- end .wrap -->
-      <div class="landing-scroll-down-affordance">
-        <a class="landing-down-arrow" href="#extending-android-to-landingables">
-          <img src="/wear/images/carrot.png" alt="Scroll down to read more">
-        </a>
-      </div>
-    </div> <!-- end .landing-section .landing-hero -->
-  </div> <!-- end .landing-hero-container -->
-
-
-    <div class="landing-rest-of-page">
-      <div class="landing-section" id="extending-android-to-landingables">
-        <div class="wrap">
-          <div class="landing-section-header">
-            <div class="landing-h1">See What's New</div>
-            <div class="landing-subhead">
-              Take advantage of all the new capabilities, which are focused on design and performance.
-            </div>
-          </div>
-
-          <div class="landing-body">
-
-            <div class="landing-breakout cols">
-              <div class="col-4">
-                <img src="/preview/images/material.png" style="opacity:.6" alt="">
-                <p>A New UI Design</p>
-                <p class="landing-small">
-                  Create a consistent experience across mobile and the web with
-                   material design, the new Google-wide standard.
-                </p>
-                <p class="landing-small">
-                  <a href="/preview/material/index.html">Learn about material</a>
-                </p>
-              </div>
-              <div class="col-4">
-                <img src="/preview/images/art.png" alt="">
-                <p>A Rehauled Runtime</p>
-                <p class="landing-small">
-                  Test your apps and get them ready for <b>ART</b> (<b>A</b>ndroid <b>R</b>un<b>t</b>ime),
-                  the default runtime in the next release.
-
-                </p>
-                <p class="landing-small">
-                  <a href="/preview/api-overview.html#ART">Learn about ART</a>
-                </p>
-              </div>
-              <div class="col-4">
-                <img src="/preview/images/notifications.png" alt="">
-                <p style="width:230px">Enhanced Notifications</p>
-                <p class="landing-small">
-                   Get more control over where notifications appear,
-                   how they look, and automatic syncing to non-handheld devices.
-                </p>
-                <p class="landing-small">
-                  <a href="/preview/api-overview.html#UI">Learn more</a>
-                </p>
-              </div>
-              <div class="col-4">
-                <img src="/preview/images/volta.png" alt="">
-                <p>Project Volta</p>
-                <p class="landing-small">
-                  We've tuned the platform to be more energy efficient and
-                  to give you more control over resource usage.
-                </p>
-                <p class="landing-small">
-                  <a href="/preview/api-overview.html#Power">Learn more</a>
-                </p>
-              </div>
-            </div>
-              <p>See the <a href="{@docRoot}preview/api-overview.html">API overview</a> for more information
-              on the rest of the new and updated features.</p>
-          </div>
-        </div> <!-- end .wrap -->
-      </div> <!-- end .landing-section -->
-
-
-
-      <div class="landing-section landing-gray-background">
-        <div class="wrap">
-          <div class="landing-section-header">
-            <div class="landing-h1">Get Your Apps Ready</div>
-            <div class="landing-subhead">
-              <p>We're giving you an early look at the SDK, so you can test your apps and build in new features.</p>
-            </div>
-          </div>
-          <div class="landing-body">
-             <p>You'll get the system images for the Nexus 5, Nexus 7 (v2),
-             and the emulator to take the new platform for a spin. In addition, you'll have
-             access to all the APIs with a preview build of the SDK.
-            </p>
-
-            <p>Check out the getting started, developer guides, and reference documentation
-            for all the information you need to get up and running.</p>
-
-            <a href="/preview/setup-sdk.html" class="landing-button landing-secondary" style="margin-top: 20px;">
-              Get Started
-            </a>
-          </div>
-        </div>
-      </div>
-    <div class="landing-section">
-        <div class="wrap">
-          <div class="cols">
-            <div class="landing-body">
-              <div class="col-3-wide">
-                  <a target="_blank" href="http://submit-bugs!">
-                    <img class="landing-social-image" src="{@docRoot}preview/images/bugs.png" alt="">
-                  </a>
-                <div class="landing-social-copy">
-                  <p>Issue Tracker</p>
-                  <p class="landing-small">
-                  Let us know when you encounter problems, so we can fix them and make
-                  the platform better for you and your users.
-                    </p><p class="landing-small">
-                      <a target="_blank" href="http://submit-bugs!">
-                      Report Issues</a>
-                    </p>
-                  <p></p>
-                </div>
-              </div>
-              <div class="col-3-wide">
-                <a target="_blank" href="http://plus.google.com">
-                  <img class="landing-social-image" src="//www.google.com/images/icons/product/gplus-128.png" alt="">
-                </a>
-                <div class="landing-social-copy">
-                  <p>Google+ </p>
-                  <p class="landing-small">
-                    Join the community of Android developers testing out the L Developer Preview and
-                    share your thoughts and experiences.
-                  </p><p class="landing-small">
-                    <a target="_blank" href="http://plus.google.com">
-                    Discuss on Google+</a>
-                    </p>
-                </div>
-              </div>
-              <div class="col-3-wide">
-                <a target="_blank" href="{@docRoot}preview/release-notes.html">
-                  <img class="landing-social-image" src="{@docRoot}preview/images/updates.png" alt="">
-                </a>
-                <div class="landing-social-copy">
-                  <p>Support and Updates</p>
-                  <p class="landing-small">
-                  Updates to the L Developer Preview are delivered
-                  in the Android SDK Manager. Check back periodically
-                  for news about the changes.
-                  </p>
-                  <p class="landing-small">
-                    <a target="_blank" href="{@docRoot}preview/support.html">Get Support</a>
-                  </p>
-                </div>
-              </div>
-            </div>
-          </div>
-        </div> <!-- end .wrap -->
-      </div>
-
-    <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
-      <div class="layout-content-col col-16" style="padding-top:4px">
-        <style>#___plusone_0 {float:right !important;}</style>
-        <div class="g-plusone" data-size="medium"></div>
-      </div>
-    </div>
-    <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
-      <div id="copyright">
-        Except as noted, this content is
-        licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
-        Creative Commons Attribution 2.5</a>. For details and
-        restrictions, see the <a href="/license.html">Content
-        License</a>.
-      </div>
-    </div>
-  </div> <!-- end landing-body-content -->
-
-  <script>
-  $("a.landing-down-arrow").on("click", function(e) {
-    $("body").animate({
-      scrollTop: $(".preview-hero").height() + 76
-    }, 1000, "easeOutQuint");
-    e.preventDefault();
-  });
-  </script>
\ No newline at end of file
diff --git a/docs/html/preview/license.jd b/docs/html/preview/license.jd
new file mode 100644
index 0000000..5ff52ba
--- /dev/null
+++ b/docs/html/preview/license.jd
@@ -0,0 +1,143 @@
+page.title=License Agreement
+
+@jd:body
+
+<p>
+To get started with the Android SDK Preview, you must agree to the following terms and conditions. 
+As described below, please note that this is a preview version of the Android SDK, subject to change, that you use at your own risk.  The Android SDK Preview is not a stable release, and may contain errors and defects that can result in serious damage to your computer systems, devices and data.
+</p>
+
+<p>
+This is the Android SDK Preview License Agreement (the “License Agreement”).
+</p>
+<div class="sdk-terms" style="height:auto;border:0;padding:0;width:700px">
+1. Introduction
+
+1.1 The Android SDK Preview (referred to in the License Agreement as the “Preview” and specifically including the Android system files, packaged APIs, and Preview library files, if and when they are made available) is licensed to you subject to the terms of the License Agreement. The License Agreement forms a legally binding contract between you and Google in relation to your use of the Preview.
+
+1.2 "Android" means the Android software stack for devices, as made available under the Android Open Source Project, which is located at the following URL: http://source.android.com/, as updated from time to time.
+
+1.3 "Google" means Google Inc., a Delaware corporation with principal place of business at 1600 Amphitheatre Parkway, Mountain View, CA 94043, United States.
+
+2. Accepting the License Agreement
+
+2.1 In order to use the Preview, you must first agree to the License Agreement. You may not use the Preview if you do not accept the License Agreement.
+
+2.2 By clicking to accept and/or using the Preview, you hereby agree to the terms of the License Agreement.
+
+2.3 You may not use the Preview and may not accept the License Agreement if you are a person barred from receiving the Preview under the laws of the United States or other countries including the country in which you are resident or from which you use the Preview.
+
+2.4 If you will use the Preview internally within your company or organization you agree to be bound by the License Agreement on behalf of your employer or other entity, and you represent and warrant that you have full legal authority to bind your employer or such entity to the License Agreement. If you do not have the requisite authority, you may not accept the License Agreement or use the Preview on behalf of your employer or other entity.
+
+3. Preview License from Google
+
+3.1 Subject to the terms of the License Agreement, Google grants you a royalty-free, non-assignable, non-exclusive, non-sublicensable, limited, revocable license to use the Preview, personally or internally within your company or organization, solely to develop applications to run on the Android platform.
+
+3.2 You agree that Google or third parties owns all legal right, title and interest in and to the Preview, including any Intellectual Property Rights that subsist in the Preview. "Intellectual Property Rights" means any and all rights under patent law, copyright law, trade secret law, trademark law, and any and all other proprietary rights. Google reserves all rights not expressly granted to you.
+
+3.3 You may not use the Preview for any purpose not expressly permitted by the License Agreement. Except to the extent required by applicable third party licenses, you may not: (a) copy (except for backup purposes), modify, adapt, redistribute, decompile, reverse engineer, disassemble, or create derivative works of the Preview or any part of the Preview; or (b) load any part of the Preview onto a mobile handset or any other hardware device except a personal computer, combine any part of the Preview with other software, or distribute any software or device incorporating a part of the Preview.
+
+3.4 You agree that you will not take any actions that may cause or result in the fragmentation of Android, including but not limited to distributing, participating in the creation of, or promoting in any way a software development kit derived from the Preview.
+
+3.5 Use, reproduction and distribution of components of the Preview licensed under an open source software license are governed solely by the terms of that open source software license and not the License Agreement. You agree to remain a licensee in good standing in regard to such open source software licenses under all the rights granted and to refrain from any actions that may terminate, suspend, or breach such rights.
+
+3.6 You agree that the form and nature of the Preview that Google provides may change without prior notice to you and that future versions of the Preview may be incompatible with applications developed on previous versions of the Preview. You agree that Google may stop (permanently or temporarily) providing the Preview (or any features within the Preview) to you or to users generally at Google's sole discretion, without prior notice to you.
+
+3.7 Nothing in the License Agreement gives you a right to use any of Google's trade names, trademarks, service marks, logos, domain names, or other distinctive brand features.
+
+3.8 You agree that you will not remove, obscure, or alter any proprietary rights notices (including copyright and trademark notices) that may be affixed to or contained within the Preview.
+
+4. Use of the Preview by You
+
+4.1 Google agrees that nothing in the License Agreement gives Google any right, title or interest from you (or your licensors) under the License Agreement in or to any software applications that you develop using the Preview, including any intellectual property rights that subsist in those applications.
+
+4.2 You agree to use the Preview and write applications only for purposes that are permitted by (a) the License Agreement, and (b) any applicable law, regulation or generally accepted practices or guidelines in the relevant jurisdictions (including any laws regarding the export of data or software to and from the United States or other relevant countries). 
+
+4.3 You agree that if you use the Preview to develop applications, you will protect the privacy and legal rights of users. If users provide you with user names, passwords, or other login information or personal information, you must make the users aware that the information will be available to your application, and you must provide legally adequate privacy notice and protection for those users. If your application stores personal or sensitive information provided by users, it must do so securely. If users provide you with Google Account information, your application may only use that information to access the user's Google Account when, and for the limited purposes for which, each user has given you permission to do so.
+
+4.4 You agree that you will not engage in any activity with the Preview, including the development or distribution of an application, that interferes with, disrupts, damages, or accesses in an unauthorized manner the servers, networks, or other properties or services of Google or any third party.
+
+4.5 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any data, content, or resources that you create, transmit or display through Android and/or applications for Android, and for the consequences of your actions (including any loss or damage which Google may suffer) by doing so.
+
+4.6 You agree that you are solely responsible for (and that Google has no responsibility to you or to any third party for) any breach of your obligations under the License Agreement, any applicable third party contract or Terms of Service, or any applicable law or regulation, and for the consequences (including any loss or damage which Google or any third party may suffer) of any such breach.
+
+4.7 The Preview is in development, and your testing and feedback are an important part of the development process. By using the Preview, you acknowledge that implementation of some features are still under development and that you should not rely on the Preview having the full functionality of a stable release. You agree not to publicly distribute or ship any application using this Preview as this Preview will no longer be supported after the official Android SDK is released.
+
+5. Your Developer Credentials
+
+5.1 You agree that you are responsible for maintaining the confidentiality of any developer credentials that may be issued to you by Google or which you may choose yourself and that you will be solely responsible for all applications that are developed under your developer credentials.
+
+6. Privacy and Information
+
+6.1 In order to continually innovate and improve the Preview, Google may collect certain usage statistics from the software including but not limited to a unique identifier, associated IP address, version number of the software, and information on which tools and/or services in the Preview are being used and how they are being used. Before any of this information is collected, the Preview will notify you and seek your consent. If you withhold consent, the information will not be collected.
+
+6.2 The data collected is examined in the aggregate to improve the Preview and is maintained in accordance with Google's Privacy Policy located at http://www.google.com/policies/privacy/.
+
+7. Third Party Applications
+
+7.1 If you use the Preview to run applications developed by a third party or that access data, content or resources provided by a third party, you agree that Google is not responsible for those applications, data, content, or resources. You understand that all data, content or resources which you may access through such third party applications are the sole responsibility of the person from which they originated and that Google is not liable for any loss or damage that you may experience as a result of the use or access of any of those third party applications, data, content, or resources.
+
+7.2 You should be aware the data, content, and resources presented to you through such a third party application may be protected by intellectual property rights which are owned by the providers (or by other persons or companies on their behalf). You may not modify, rent, lease, loan, sell, distribute or create derivative works based on these data, content, or resources (either in whole or in part) unless you have been specifically given permission to do so by the relevant owners.
+
+7.3 You acknowledge that your use of such third party applications, data, content, or resources may be subject to separate terms between you and the relevant third party.
+
+8. Using Google APIs
+
+8.1 Google APIs
+
+8.1.1 If you use any API to retrieve data from Google, you acknowledge that the data may be protected by intellectual property rights which are owned by Google or those parties that provide the data (or by other persons or companies on their behalf). Your use of any such API may be subject to additional Terms of Service. You may not modify, rent, lease, loan, sell, distribute or create derivative works based on this data (either in whole or in part) unless allowed by the relevant Terms of Service.
+
+8.1.2 If you use any API to retrieve a user's data from Google, you acknowledge and agree that you shall retrieve data only with the user's explicit consent and only when, and for the limited purposes for which, the user has given you permission to do so.
+
+9. Terminating the License Agreement
+
+9.1 the License Agreement will continue to apply until terminated by either you or Google as set out below.
+
+9.2 If you want to terminate the License Agreement, you may do so by ceasing your use of the Preview and any relevant developer credentials.
+
+9.3 Google may at any time, terminate the License Agreement, with or without cause, upon notice to you.
+
+9.4 The License Agreement will automatically terminate without notice or other action upon the earlier of:
+(A) when Google ceases to provide the Preview or certain parts of the Preview to users in the country in which you are resident or from which you use the service; and
+(B) Google issues a final release version of the Android SDK.
+
+9.5 When the License Agreement is terminated, the license granted to you in the License Agreement will terminate, you will immediately cease all use of the Preview, and the provisions of paragraphs 10, 11, 12 and 14 shall survive indefinitely.
+
+10. DISCLAIMERS
+
+10.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT YOUR USE OF THE PREVIEW IS AT YOUR SOLE RISK AND THAT THE PREVIEW IS PROVIDED "AS IS" AND "AS AVAILABLE" WITHOUT WARRANTY OF ANY KIND FROM GOOGLE.
+
+10.2 YOUR USE OF THE PREVIEW AND ANY MATERIAL DOWNLOADED OR OTHERWISE OBTAINED THROUGH THE USE OF THE PREVIEW IS AT YOUR OWN DISCRETION AND RISK AND YOU ARE SOLELY RESPONSIBLE FOR ANY DAMAGE TO YOUR COMPUTER SYSTEM OR OTHER DEVICE OR LOSS OF DATA THAT RESULTS FROM SUCH USE. WITHOUT LIMITING THE FOREGOING, YOU UNDERSTAND THAT THE PREVIEW IS NOT A STABLE RELEASE AND MAY CONTAIN ERRORS, DEFECTS AND SECURITY VULNERABILITIES THAT CAN RESULT IN SIGNIFICANT DAMAGE, INCLUDING THE COMPLETE, IRRECOVERABLE LOSS OF USE OF YOUR COMPUTER SYSTEM OR OTHER DEVICE.
+
+10.3 GOOGLE FURTHER EXPRESSLY DISCLAIMS ALL WARRANTIES AND CONDITIONS OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES AND CONDITIONS OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
+
+11. LIMITATION OF LIABILITY
+
+11.1 YOU EXPRESSLY UNDERSTAND AND AGREE THAT GOOGLE, ITS SUBSIDIARIES AND AFFILIATES, AND ITS LICENSORS SHALL NOT BE LIABLE TO YOU UNDER ANY THEORY OF LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR EXEMPLARY DAMAGES THAT MAY BE INCURRED BY YOU, INCLUDING ANY LOSS OF DATA, WHETHER OR NOT GOOGLE OR ITS REPRESENTATIVES HAVE BEEN ADVISED OF OR SHOULD HAVE BEEN AWARE OF THE POSSIBILITY OF ANY SUCH LOSSES ARISING.
+
+12. Indemnification
+
+12.1 To the maximum extent permitted by law, you agree to defend, indemnify and hold harmless Google, its affiliates and their respective directors, officers, employees and agents from and against any and all claims, actions, suits or proceedings, as well as any and all losses, liabilities, damages, costs and expenses (including reasonable attorneys’ fees) arising out of or accruing from (a) your use of the Preview, (b) any application you develop on the Preview that infringes any Intellectual Property Rights of any person or defames any person or violates their rights of publicity or privacy, and (c) any non-compliance by you of the License Agreement.
+
+13. Changes to the License Agreement
+
+13.1 Google may make changes to the License Agreement as it distributes new versions of the Preview. When these changes are made, Google will make a new version of the License Agreement available on the website where the Preview is made available.
+
+14. General Legal Terms
+
+14.1 the License Agreement constitutes the whole legal agreement between you and Google and governs your use of the Preview (excluding any services which Google may provide to you under a separate written agreement), and completely replaces any prior agreements between you and Google in relation to the Preview.
+
+14.2 You agree that if Google does not exercise or enforce any legal right or remedy which is contained in the License Agreement (or which Google has the benefit of under any applicable law), this will not be taken to be a formal waiver of Google's rights and that those rights or remedies will still be available to Google.
+
+14.3 If any court of law, having the jurisdiction to decide on this matter, rules that any provision of the License Agreement is invalid, then that provision will be removed from the License Agreement without affecting the rest of the License Agreement. The remaining provisions of the License Agreement will continue to be valid and enforceable.
+
+14.4 You acknowledge and agree that each member of the group of companies of which Google is the parent shall be third party beneficiaries to the License Agreement and that such other companies shall be entitled to directly enforce, and rely upon, any provision of the License Agreement that confers a benefit on (or rights in favor of) them. Other than this, no other person or company shall be third party beneficiaries to the License Agreement.
+
+14.5 EXPORT RESTRICTIONS. THE PREVIEW IS SUBJECT TO UNITED STATES EXPORT LAWS AND REGULATIONS. YOU MUST COMPLY WITH ALL DOMESTIC AND INTERNATIONAL EXPORT LAWS AND REGULATIONS THAT APPLY TO THE PREVIEW. THESE LAWS INCLUDE RESTRICTIONS ON DESTINATIONS, END USERS AND END USE.
+
+14.6 The License Agreement may not be assigned or transferred by you without the prior written approval of Google, and any attempted assignment without such approval will be void. You shall not delegate your responsibilities or obligations under the License Agreement without the prior written approval of Google.
+
+14.7 The License Agreement, and your relationship with Google under the License Agreement, shall be governed by the laws of the State of California without regard to its conflict of laws provisions. You and Google agree to submit to the exclusive jurisdiction of the courts located within the county of Santa Clara, California to resolve any legal matter arising from the License Agreement. Notwithstanding this, you agree that Google shall still be allowed to apply for injunctive remedies (or an equivalent type of urgent legal relief) in any jurisdiction.
+
+
+</div>
\ No newline at end of file
diff --git a/docs/html/preview/material/animations.jd b/docs/html/preview/material/animations.jd
index cee782a..b8d063b 100644
--- a/docs/html/preview/material/animations.jd
+++ b/docs/html/preview/material/animations.jd
@@ -10,8 +10,9 @@
   <li><a href="#reveal">Reveal Effect</a></li>
   <li><a href="#transitions">Activity Transitions</a></li>
   <li><a href="#curvedmotion">Curved Motion</a></li>
-  <li><a href="#viewstate">View State Changes</a></li>
+  <li><a href="#viewstate">Animating View State Changes</a></li>
   <li><a href="#drawabletint">Drawable Tinting</a></li>
+  <li><a href="#colorextract">Extracting Colors from an Image</a></li>
 </ol>
 </div>
 </div>
@@ -32,19 +33,26 @@
 
 <h2 id="touch">Touch Feedback</h2>
 
-<p>In the Android L Developer Preview the default touch feedback animations for buttons use the new
+<p>The default touch feedback animations for buttons use the new
 <code>RippleDrawable</code> class, which transitions between different states with a ripple
 effect.</p>
 
-<p>To use this functionality in your custom views, create a <code>RippleDrawable</code> and set
-it as the background of your view. You can define a <code>RippleDrawable</code> as an XML resource
-using the <code>ripple</code> element.</p>
+<p>In most cases, this functionality should be applied in your view XML by specifying the
+background as <code>?android:attr/selectableItemBackground</code> for a bounded ripple or
+<code>?android:attr/selectableItemBackgroundBorderless</code> for a ripple that extends beyond
+the view bounds. You can also create a <code>RippleDrawable</code> and set
+it as the background of your view. Alternatively, you can define a <code>RippleDrawable</code>
+as an XML resource using the <code>ripple</code> element. The
+Android L Developer Preview animates the selection color with a ripple effect.</p>
+
+<p>You can assign a color to <code>RippleDrawable</code> objects. To change the default touch
+feedback color, use the theme's <code>android:colorControlHighlight</code> attribute.</p>
 
 
 <h2 id="reveal">Reveal Effect</h2>
 
-<p>The <code>View.createRevealAnimator</code> method enables you to animate a clipping circle
-to reveal or hide a view.</p>
+<p>The <code>ViewAnimationUtils.createCircularReveal</code> method enables you to animate a
+clipping circle to reveal or hide a view.</p>
 
 <p>To reveal a previously invisible view using this effect:</p>
 
@@ -61,7 +69,8 @@
 
 // create and start the animator for this view
 // (the start radius is zero)
-ValueAnimator anim = myView.createRevealAnimator(cx, cy, 0, finalRadius);
+ValueAnimator anim =
+    ViewAnimationUtils.createCircularReveal(myView, cx, cy, 0, finalRadius);
 anim.start();
 </pre>
 
@@ -79,7 +88,8 @@
 int initialRadius = myView.getWidth();
 
 // create the animation (the final radius is zero)
-ValueAnimator anim = myView.createRevealAnimator(cx, cy, initialRadius, 0);
+ValueAnimator anim =
+    ViewAnimationUtils.createCircularReveal(myView, cx, cy, initialRadius, 0);
 
 // make the view invisible when the animation is done
 anim.addListener(new AnimatorListenerAdapter() {
@@ -97,25 +107,48 @@
 
 <h2 id="transitions">Activity Transitions</h2>
 
-<p>The Android L Developer Preview enables your app to customize the default animations for
-activity transitions. You can specify custom animations for enter and exit transitions and for
+<p>You can specify custom animations for enter and exit transitions and for
 transitions of shared elements between activities.</p>
 
 <ul>
-  <li>An <strong>enter</strong> transition determines how views in an activity enter the scene.
-  For example, in the <em>explode</em> enter transition the views enter the scene from outside
-  and fly in towards the center of the screen.</li>
+<li>An <strong>enter</strong> transition determines how views in an activity enter the scene.
+For example, in the <em>explode</em> enter transition, the views enter the scene from the outside
+and fly in towards the center of the screen.</li>
 
-  <li>An <strong>exit</strong> transition determines how views in an activity exit the scene. For
-  example, in the <em>explode</em> exit transition the views exit the scene away from the
-  center.</li>
+<li>An <strong>exit</strong> transition determines how views in an activity exit the scene. For
+  example, in the <em>explode</em> exit transition, the views exit the scene away from the
+center.</li>
 
-  <li>A <strong>shared elements</strong> transition determines how views that are shared between
-  two activities transition between these activities. For example, if two activities have the same
-  image in different positions and sizes, the <em>moveImage</em> shared element transition
-  translates and scales the image smoothly between these activities.</li>
+<li>A <strong>shared elements</strong> transition determines how views that are shared between
+two activities transition between these activities. For example, if two activities have the same
+image in different positions and sizes, the <em>moveImage</em> shared element transition
+translates and scales the image smoothly between these activities.</li>
 </ul>
 
+<p>The Android L Developer Preview supports these enter and exit transitions:</p>
+
+<ul>
+<li><em>explode</em> - Moves views in or out from the center of the scene.</li>
+<li><em>slide</em> - Moves views in or out from one of the edges of the scene.</li>
+<li><em>fade</em> - Mades views in or out of the scene.</li>
+</ul>
+
+<p>Any transition that extends the <code>android.transition.Visibility</code> class is supported
+as an enter or exit transition. For more information, see the API reference for the
+<code>android.transition.Transition</code> class.</p>
+
+<p>The Android L Developer Preview also supports these shared elements transitions:</p>
+
+<ul>
+<li><em>changeBounds</em> - Animates the changes in layout bounds of target views.</li>
+<li><em>changeClipBounds</em> - Animates the changes in clip bounds of target views.</li>
+<li><em>changeTransform</em> - Animates the changes in scale and rotation of target views.</li>
+<li><em>moveImage</em> - Animates changes in size and scale type for an image view.</li>
+</ul>
+
+<p>When you enable activity transitions in your app, the default cross-fading transition is
+activated between the entering and exiting activities.</p>
+
 <img src="/preview/material/images/SceneTransition.png" alt=""
      id="figure1" style="width:600px;margin-top:20px"/>
 <p class="img-caption">
@@ -125,7 +158,8 @@
 <h3>Specify custom transitions</h3>
 
 <p>First, enable window content transitions with the <code>android:windowContentTransitions</code>
-attribute when you define a style that inherits from the material theme:</p>
+attribute when you define a style that inherits from the material theme. You can also specify
+enter, exit, and shared element transitions in your style definition:</p>
 
 <pre>
 &lt;style name="BaseAppTheme" parent="android:Theme.Material">
@@ -144,21 +178,13 @@
 &lt;/style>
 </pre>
 
-<p>You can also specify enter, exit, and shared element transitions in your style definition.
-The <code>move_image</code> transition in this example is defined as follows:</p>
+<p>The <code>move_image</code> transition in this example is defined as follows:</p>
 
 <pre>
 &lt;!-- res/transition/move_image.xml -->
 &lt;!-- (see also Shared Transitions below) -->
 &lt;transitionSet xmlns:android="http://schemas.android.com/apk/res/android">
-  &lt;moveImage>
-    &lt;targets>
-      &lt;!-- shared view in the first activity -->
-      &lt;target android:targetId="@id/image_small" />
-      &lt;!-- shared view in the second activity -->
-      &lt;target android:targetId="@id/image_big" />
-    &lt;/targets>
-  &lt;/moveImage>
+  &lt;moveImage/>
 &lt;/transitionSet>
 </pre>
 
@@ -170,7 +196,7 @@
 <code>Window.requestFeature</code> method:</p>
 
 <pre>
-// inside your activity
+// inside your activity (if you did not enable transitions in your theme)
 getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
 
 // set an exit transition
@@ -187,6 +213,20 @@
   <li><code>Window.setSharedElementExitTransition</code></li>
 </ul>
 
+<p>The <code>setExitTransition</code> and <code>setSharedElementExitTransition</code> methods
+define the exit transition for the calling activity. The <code>setEnterTransition</code> and
+<code>setSharedElementEnterTransition</code> methods define the enter transition for the called
+activity.</p>
+
+<p>To get the full effect of a transition, you must enable window content transitions on both the
+calling and called activities. Otherwise, the calling activity will start the exit transition,
+but then you'll see a window transition (like scale or fade).</p>
+
+<p>To start an enter transition as soon as possible, use the
+<code>Window.setAllowEnterTransitionOverlap</code> method on the called activity. This lets you
+have more dramatic enter transitions. The same applies for the calling activity and exit
+transitions with the <code>Window.setAllowExitTransitionOverlap</code> method.</p>
+
 <h3>Start an activity using transitions</h3>
 
 <p>If you enable transitions and set an exit transition for an activity, the transition is activated
@@ -201,7 +241,7 @@
 <ol>
 <li>Enable window content transitions in your style.</li>
 <li>Specify a shared elements transition in your style.</li>
-<li>Define your transition as an XML resource specifying the IDs of the target views.</li>
+<li>Define your transition as an XML resource.</li>
 <li>Assign a common name to the shared elements in both layouts with the
     <code>android:viewName</code> attribute.</li>
 <li>Use the <code>ActivityOptions.makeSceneTransitionAnimation</code> method.</li>
@@ -212,7 +252,7 @@
 final View imgContainerView = findViewById(R.id.img_container);
 
 // get the common element for the transition in this activity
-final View androidRobotView = findViewById(R.id.android_robot_img);
+final View androidRobotView = findViewById(R.id.image_small);
 
 // define a click listener
 imgContainerView.setOnClickListener(new View.OnClickListener() {
@@ -232,6 +272,9 @@
 <p>For shared dynamic views that you generate in your code, use the <code>View.setViewName</code>
 method to specify a common element name in both activities.</p>
 
+<p>To reverse the scene transition animation when you finish the second activity, call the
+<code>Activity.finishAfterTransition</code> method instead of <code>Activity.finish</code>.</p>
+
 <h3>Multiple shared elements</h3>
 
 <p>To make a scene transition animation between two activities that have more than one shared
@@ -241,12 +284,8 @@
 
 <pre>
 ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(this,
-    new Pair[] {
         Pair.create(view1, "agreedName1"),
-        Pair.create(view2, "agreedName2"),
-        ...
-    }
-);
+        Pair.create(view2, "agreedName2"));
 </pre>
 
 
@@ -279,7 +318,7 @@
 </ul>
 
 <p>You can pass a <code>PathInterpolator</code> object to the
-<code>Animation.setInterpolation</code> method.</p>
+<code>Animator.setInterpolation</code> method.</p>
 
 <p>The <code>ObjectAnimator</code> class has new constructors that enable you to animate
 coordinates along a path using two or more properties at once. For example, the following animator
@@ -293,20 +332,20 @@
 </pre>
 
 
-<h2 id="viewstate">View State Changes</h2>
+<h2 id="viewstate">Animating View State Changes</h2>
 
 <p>The new <code>StateListAnimator</code> class lets you define animators that run when the state
 of a view changes. The following example shows how to define an <code>StateListAnimator</code> as
 an XML resource:</p>
 
 <pre>
-&lt;!-- animate the elevation property of a view when pressed -->
+&lt;!-- animate the translationZ property of a view when pressed -->
 &lt;selector xmlns:android="http://schemas.android.com/apk/res/android">
   &lt;item android:state_pressed="true">
     &lt;set>
-      &lt;objectAnimator android:propertyName="elevation"
+      &lt;objectAnimator android:propertyName="translationZ"
         android:duration="100"
-        android:valueTo="60"
+        android:valueTo="2"
         android:valueType="floatType"/>
         &lt;!-- you could have other objectAnimator elements
              here for "x" and "y", or other properties -->
@@ -316,15 +355,19 @@
     android:state_pressed="false"
     android:state_focused="true">
     &lt;set>
-      &lt;objectAnimator android:propertyName="elevation"
+      &lt;objectAnimator android:propertyName="translationZ"
         android:duration="100"
-        android:valueTo="10"
+        android:valueTo="2"
         android:valueType="floatType"/>
     &lt;/set>
   &lt;/item>
 &lt;/selector>
 </pre>
 
+<p class="note"><strong>Note:</strong> There is a known issue in the L Developer Preview release
+that requires valueFrom values to be provided in StateListAnimator animations to get the correct
+behavior.</p>
+
 <p>The new <code>AnimatedStateListDrawable</code> class lets you create drawables that show
 animations between state changes of the associated view. Some of the system widgets in the
 Android L Developer Preview use these animations by default. The following example shows how
@@ -337,9 +380,9 @@
 
     &lt;!-- provide a different drawable for each state-->
     &lt;item android:id="@+id/pressed" android:drawable="@drawable/drawableP"
-        android:state-pressed="true"/>
+        android:state_pressed="true"/>
     &lt;item android:id="@+id/focused" android:drawable="@drawable/drawableF"
-        android:state-focused="true"/>
+        android:state_focused="true"/>
     &lt;item android:id="@id/default"
         android:drawable="@drawable/drawableD"/>
 
@@ -358,21 +401,41 @@
 
 <h2 id="drawabletint">Drawable Tinting</h2>
 
-<p>The Android L Developer Preview enables you to define bitmaps as an alpha mask and to tint
-them using a color resource or a theme attribute that resolves to a color resource. You can
-create these assets only once and color them automatically to match your theme.</p>
+<p>The Android L Developer Preview enables you to define bitmaps or nine-patches as alpha masks and
+to tint them using a color resource or a theme attribute that resolves to a color resource (for
+example, <code>?android:attr/colorPrimary</code>). You can create these assets only once and color them
+automatically to match your theme.</p>
 
-<p>To apply a tint to a bitmap in your code, use the <code>setTint</code> method in these
-classes:</p>
+<p>To apply a tint to a bitmap, use the <code>setTint</code> method or the <code>android:tint</code>
+attribute for <code>BitmapDrawable</code> and <code>NinePatchDrawable</code>.</p>
+
+<p>The <code>setTint</code> method also lets you set the Porter-Duff mode used to blend the
+tint color for <code>NinePatchDrawable</code> and <code>BitmapDrawable</code> objects in your code.
+To set the tint mode in your layouts, use the <code>android:tintMode</code> attribute.</p>
+
+
+<h2 id="colorextract">Extracting Prominent Colors from an Image</h2>
+
+<p>The Android L Developer Preview Support Library includes the <code>Palette</code> class,
+which lets you extract prominent colors from an image. This class extracts the following
+prominent colors:</p>
 
 <ul>
-<li><code>PaintDrawable</code></li>
-<li><code>NinePatchDrawable</code></li>
-<li><code>RippleDrawable</code></li>
+<li>Vibrant</li>
+<li>Vibrant dark</li>
+<li>Vibrant light</li>
+<li>Muted</li>
+<li>Muted dark</li>
+<li>Muted light</li>
 </ul>
 
-<p>In your layouts, use the <code>android:tint</code> attribute instead.</p>
+<p>To extract these colors, pass a <code>Bitmap</code> object to the
+<code>Palette.generate</code> static method in the background thread where you load your images.
+If you can't use that thread, call the <code>Palette.generateAsync</code> method instead and
+provide a listener.</p>
 
-<p>The <code>setTint</code> method also lets you set the tint blending mode for
-<code>NinePatchDrawable</code> and <code>RippleDrawable</code> objects in your code. To set the
-tint mode in your layouts, use the <code>android:tintMode</code> attribute.</p>
+<p>To retrieve the prominent colors from the image, use the getter methods in the
+<code>Palette</code> class, such as <code>Palette.getVibrantColor</code>.</p>
+
+<p>For more information, see the API reference for the
+<code>android.support.v7.graphics.Palette</code> class.</p>
\ No newline at end of file
diff --git a/docs/html/preview/material/compatibility.jd b/docs/html/preview/material/compatibility.jd
index ce04e9e..fb97112 100644
--- a/docs/html/preview/material/compatibility.jd
+++ b/docs/html/preview/material/compatibility.jd
@@ -14,7 +14,7 @@
 </div>
 </div>
 
-<p>The new material design features (like the material theme and custom animations) are only
+<p>The new material design features (like the material theme and activity transitions) are only
 available in the Android L Developer Preview. However, you can design your apps to make use of
 these features when running on devices with the Android L Developer Preview and still be
 compatible with previous releases of Android.</p>
@@ -49,15 +49,34 @@
 and your alternative layout files for earlier versions of Android inside <code>res/layout/</code>.
 Alternative layouts have the same file name.</p>
 
+<p>To avoid duplication of code, define your styles inside <code>res/values/</code> and modify the
+styles in <code>res/values-v21/</code> for the new APIs.</p>
+
 
 <h2 id="widgets">UI Widgets</h2>
 
 <p>The <code>RecyclerView</code> and <code>CardView</code> widgets are included in the Android L
-Developer Preview Support Library, so they are available in earlier versions of Android.</p>
+Developer Preview Support Library, so they are available in earlier versions of Android with
+these limitations:</p>
+
+<ul>
+<li><code>CardView</code> falls back to a programmatic shadow implementation using additional padding.</li>
+<li><code>CardView</code> does not clip its children views that intersect with rounded corners.</li>
+</ul>
+
+<p>These limitations do not apply to the Android L Developer Preview.</p>
 
 
 <h2 id="animation">Animation APIs</h2>
 
-<p>The new APIs for custom animations are only available in the Android L Developer Preview. To
-preserve compatibility with earlier verisons of Android, check the system version at runtime before
-you invoke these APIs.</p>
\ No newline at end of file
+<p>The following new APIs are only available in the Android L Developer Preview:</p>
+
+<ul>
+<li>Activity transitions</li>
+<li>Touch feedback</li>
+<li>Reveal animations</li>
+<li>Path-based animations</li>
+</ul>
+
+<p>To preserve compatibility with earlier verisons of Android, check the system version at
+runtime before you invoke these APIs.</p>
\ No newline at end of file
diff --git a/docs/html/preview/material/get-started.jd b/docs/html/preview/material/get-started.jd
index 9c0e55d..7d0625e 100644
--- a/docs/html/preview/material/get-started.jd
+++ b/docs/html/preview/material/get-started.jd
@@ -8,9 +8,9 @@
 <ol>
   <li><a href="#applytheme">Apply the Material Theme</a></li>
   <li><a href="#layouts">Design Your Layouts</a></li>
-  <li><a href="#depth">Specify Depth in Your Views</a></li>
+  <li><a href="#depth">Specify Elevation in Your Views</a></li>
   <li><a href="#widgets">Use the New UI Widgets</a></li>
-  <li><a href="#apis">Use the New APIs</a></li>
+  <li><a href="#animations">Customize Your Animations</a></li>
 </ol>
 </div>
 </div>
@@ -19,7 +19,8 @@
 
 <ol>
   <li style="margin-bottom:10px">
-    Take a look at the <a href="">material design specification</a>.</li>
+    Take a look at the <a href="http://www.google.com/design/spec">material design
+    specification</a>.</li>
   <li style="margin-bottom:10px">
     Apply the material <strong>theme</strong> to your app.</li>
   <li style="margin-bottom:10px">
@@ -27,11 +28,11 @@
   <li style="margin-bottom:10px">
     Create your <strong>layouts</strong> following material design guidelines.</li>
   <li style="margin-bottom:10px">
-    Specify the <strong>depth</strong> for views to cast appropriate shadows.</li>
+    Specify the <strong>elevation</strong> of your views to cast appropriate shadows.</li>
   <li style="margin-bottom:10px">
     Use the new <strong>widgets</strong> for complex views, such as lists and cards.</li>
   <li style="margin-bottom:10px">
-    Use the new <strong>APIs</strong> to customize the animations in your app.</li>
+    Use the new APIs to customize the <strong>animations</strong> in your app.</li>
 </ol>
 
 <h3>Update Your App for the Android L Developer Preview</h3>
@@ -42,15 +43,16 @@
 
 <h3>Create New Apps for the Android L Developer Preview</h3>
 
-<p>If you are creating a new app for the Android L Developer Preview, the material design
-guidelines provide you with a solid design framework for your app. Follow these guidelines and
+<p>If you are creating a new app for the Android L Developer Preview, the <a
+href="http://www.google.com/design/spec">material design guidelines</a> provide you with a
+cohesive design framework for your app. Follow these guidelines and
 use the new functionality in the Android framework to design and develop your app.</p>
 
 
 <h2 id="applytheme">Apply the Material Theme</h2>
 
 <p>To apply the material theme in your app, specify a style that inherits from
-<code>android:theme.Material</code>:</p>
+<code>android:Theme.Material</code>:</p>
 
 <pre>
 &lt;!-- res/values/styles.xml -->
@@ -70,8 +72,8 @@
 <h2 id="layouts">Design Your Layouts</h2>
 
 <p>In addition to applying and customizing the material theme, your layouts should conform to
-the material design guidelines. When you design your layouts, pay special attention to the
-following:</p>
+the <a href="http://www.google.com/design/spec">material design guidelines</a>. When you design
+your layouts, pay special attention to the following:</p>
 
 <ul>
 <li>Baseline grids</li>
@@ -81,37 +83,36 @@
 <li>Layout structure</li>
 </ul>
 
-<p>You still define layouts inside XML files using the standard tools from the Android framework.
-For details on the material design guidelines, see the <a href="">material design
-specification</a>.</p>
 
+<h2 id="depth">Specify Elevation in Your Views</h2>
 
-<h2 id="depth">Specify Depth in Your Views</h2>
-
-<p>In the Android L Developer Preview, views can cast shadows. The elevation value of a view
-determines the size of its shadow. To set the elevation of a view, use the
+<p>Views can cast shadows, and the elevation value of a view
+determines the size of its shadow and its drawing order. To set the elevation of a view, use the
 <code>android:elevation</code> attribute in your layouts:</p>
 
 <pre>
-&lt;Button
-    android:id="@+id/my_button"
+&lt;TextView
+    android:id="@+id/my_textview"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:text="@string/next"
-    <strong>android:elevation</strong>="10dp" />
+    android:background="@color/white"
+    <strong>android:elevation</strong>="5dp" />
 </pre>
 
+<p>The new <code>translationZ</code> property lets you create animations that reflect temporary
+changes in the elevation of a view. For example, this is useful to respond to touch gestures.</p>
+
 <p>For more details, see <a href="{@docRoot}preview/material/views-shadows.html">Views and
 Shadows</a>.</p>
 
 
 <h2 id="widgets">Use the New UI Widgets</h2>
 
-<p>The Android L Developer Preview includes two new UI widgets for complex views,
-<code>RecyclerView</code> and <code>CardView</code>. <code>RecyclerView</code> is a more advanced
-version of <code>ListView</code> that provides performance improvements and is easier to use.
-<code>CardView</code> lets you show pieces of information inside cards with a consistent look
-across apps. To include a <code>CardView</code> in your layout:</p>
+<p><code>RecyclerView</code> is a more advanced version of <code>ListView</code> that provides
+performance improvements and is easier to use. <code>CardView</code> lets you show pieces of
+information inside cards with a consistent look across apps. To include a <code>CardView</code>
+in your layout:</p>
 
 <pre>
 &lt;android.support.v7.widget.CardView
@@ -126,7 +127,7 @@
 <p>For more information, see <a href="{@docRoot}preview/material/ui-widgets.html">UI Widgets</a>.</p>
 
 
-<h2 id="apis">Use the APIs to Customize Your Animations</h2>
+<h2 id="animations">Customize Your Animations</h2>
 
 <p>The Android L Developer Preview includes new APIs to create custom animations in your app.
 For example, you can enable activity transitions and define an exit transition inside an
diff --git a/docs/html/preview/material/images/MaterialDark.png b/docs/html/preview/material/images/MaterialDark.png
index 6a72280..f1018af 100644
--- a/docs/html/preview/material/images/MaterialDark.png
+++ b/docs/html/preview/material/images/MaterialDark.png
Binary files differ
diff --git a/docs/html/preview/material/images/MaterialLight.png b/docs/html/preview/material/images/MaterialLight.png
index 0e85528..4ed7d5c 100644
--- a/docs/html/preview/material/images/MaterialLight.png
+++ b/docs/html/preview/material/images/MaterialLight.png
Binary files differ
diff --git a/docs/html/preview/material/images/card_travel.png b/docs/html/preview/material/images/card_travel.png
index a804ca0..19752a8 100644
--- a/docs/html/preview/material/images/card_travel.png
+++ b/docs/html/preview/material/images/card_travel.png
Binary files differ
diff --git a/docs/html/preview/material/images/list_mail.png b/docs/html/preview/material/images/list_mail.png
index ca53ee1..bd107ff 100644
--- a/docs/html/preview/material/images/list_mail.png
+++ b/docs/html/preview/material/images/list_mail.png
Binary files differ
diff --git a/docs/html/preview/material/index.jd b/docs/html/preview/material/index.jd
index b7abcb4..d9a276f 100644
--- a/docs/html/preview/material/index.jd
+++ b/docs/html/preview/material/index.jd
@@ -3,11 +3,11 @@
 
 @jd:body
 
-<p itemprop="description">The Android L Developer Preview includes support for material design apps. Material design
-is a comprehensive guide for visual, motion, and interaction design across platforms and devices.
-To use material design in your Android apps, follow the guidelines defined in the
-<a href="">material design specification</a> and use the new components and functionality
-available in the Android L Developer Preview.</p>
+<p itemprop="description">The Android L Developer Preview includes support for material design
+apps. Material design is a comprehensive guide for visual, motion, and interaction design across
+platforms and devices. To use material design in your Android apps, follow the guidelines defined
+in the <a href="http://www.google.com/design/spec">material design specification</a> and use the
+new components and functionality available in the Android L Developer Preview.</p>
 
 <p>The Android L Developer Preview provides the following elements for you to build material
 design apps:</p>
@@ -27,13 +27,13 @@
 <!-- two columns -->
 <div style="width:700px;margin-top:25px;margin-bottom:20px">
 <div style="float:left;width:250px;margin-left:40px;margin-right:60px;">
-  <img src="{@docRoot}preview/material/images/MaterialDark.png" style="width:250px;"/>
+  <img src="{@docRoot}preview/material/images/MaterialDark.png" width="500" height="238"/>
   <div style="width:140px;margin:0 auto">
   <p style="margin-top:8px">Dark Material theme</p>
   </div>
 </div>
 <div style="float:left;width:250px;margin-right:0px;">
-  <img src="{@docRoot}preview/material/images/MaterialLight.png" style="width:250px;"/>
+  <img src="{@docRoot}preview/material/images/MaterialLight.png" width="500" height="238"/>
   <div style="width:140px;margin:0 auto">
   <p style="margin-top:8px">Light Material theme</p>
   </div>
@@ -49,12 +49,12 @@
 <!-- two columns -->
 <div style="width:700px;margin-top:25px;margin-bottom:20px">
 <div style="float:left;width:250px;margin-left:40px;margin-right:60px;">
-  <img src="{@docRoot}preview/material/images/list_mail.png" style="width:250px;"/>
+  <img src="{@docRoot}preview/material/images/list_mail.png" width="500" height="426"/>
   <p>The new <code>RecyclerView</code> widget is a more advanced version of <code>ListView</code>
-  provides performance improvements for dynamic views and is easier to use.</p>
+  that provides performance improvements for dynamic views and is easier to use.</p>
 </div>
 <div style="float:left;width:250px;margin-right:0px;">
-  <img src="{@docRoot}preview/material/images/card_travel.png" style="width:250px;"/>
+  <img src="{@docRoot}preview/material/images/card_travel.png" width="500" height="426"/>
   <p>The new <code>CardView</code> widget lets you display important pieces of information inside
   cards that have a consistent look and feel.</p>
 </div>
@@ -64,9 +64,13 @@
 
 <h3>View Shadows</h3>
 
-<p>In addition to the X and Y components, views in the Android L Developer Preview have a Z
-component. This new component represents the elevation of a view, which determines the size of
-its shadow: views with higher Z values cast bigger shadows.</p>
+<p>In addition to the X and Y properties, views in the Android L Developer Preview have a Z
+property. This new property represents the elevation of a view, which determines:</p>
+
+<ul>
+<li>The size of the shadow - Views with higher Z values cast bigger shadows.</li>
+<li>The drawing order - Views with higher Z values appear on top of other views.</li>
+</ul>
 
 <div style="width:290px;margin-left:35px;float:right">
   <div class="framed-nexus5-port-span-5">
@@ -86,7 +90,7 @@
 <p>The Android L Developer Preview provides new APIs that let you create custom animations for
 touch feedback in UI controls, view state changes, and activity transitions.</p>
 
-<p>The new animation APIs in the Android L Developer Preview let you:</p>
+<p>The new animation APIs let you:</p>
 
 <ul>
 <li style="margin-bottom:15px">
@@ -99,7 +103,7 @@
 Switch between activities with custom <strong>activity transition</strong> animations.
 </li>
 <li style="margin-bottom:15px">
-Create custom animation patterns with <strong>curved motion</strong>.
+Create more natural animations with <strong>curved motion</strong>.
 </li>
 <li style="margin-bottom:15px">
 Animate changes in one or more view properties with <strong>view state change</strong> animations.
@@ -109,9 +113,16 @@
 </li>
 </ul>
 
+<p>Touch feedback animations are built into several standard views, such as buttons. The new APIs
+let you customize these animations and add animations to your custom views.</p>
+
 
 <h3>New Capabilities for Drawables</h3>
 
 <p>The Android L Developer Preview supports <strong>drawable tinting</strong>: you can define
-bitmaps as an alpha mask and tint them using a color resource. You can create these assets only
-once and color each instance to match your theme.</p>
+bitmaps as an alpha mask and tint them using a color resource. You create these assets only
+once and color each instance to match your theme. Drawables also now support specifying most XML
+properties as <strong>theme attributes</strong>.</p>
+
+<p>The Android L Developer Preview Support Library includes a <strong>color extraction</strong>
+library that lets you automatically extract prominent colors from a bitmap image.</p>
\ No newline at end of file
diff --git a/docs/html/preview/material/theme.jd b/docs/html/preview/material/theme.jd
index b954960..5e4bcdf 100644
--- a/docs/html/preview/material/theme.jd
+++ b/docs/html/preview/material/theme.jd
@@ -8,7 +8,7 @@
 <ol>
   <li><a href="#colorpalette">Customize the Colot Palette</a></li>
   <li><a href="#statusbar">Customize the Status Bar</a></li>
-  <li><a href="#inheritance">Theme Inheritance</a></li>
+  <li><a href="#inheritance">Theme Individual Views</a></li>
 </ol>
 </div>
 </div>
@@ -21,18 +21,18 @@
   <li>Activity transition animations</li>
 </ul>
 
-<p>The Android L Developer Preview lets you easily customize the look of the material theme
-according to your brand identity with a color palette you control. You can tint the app bar and
+<p>You can customize the look of the material theme
+according to your brand identity with a color palette you control. You can tint the action bar and
 the status bar using theme attributes, as shown in Figure 1.</p>
 
-<div style="float:right;margin-left:25px;margin-top:-25px">
+<div style="float:right;margin-left:25px;margin-top:-50px">
 <img src="{@docRoot}preview/material/images/ThemeColors.png" style="width:250px"/>
-<p class="img-caption"><strong>Figure 1.</strong> Customizing the material theme.</p>
+<p class="img-caption" style="margin-bottom:0px">
+<strong>Figure 1.</strong> Customizing the material theme.</p>
 </div>
 
-<p>The system widgets have a new design and touch feedback animations. Activity transitions help
-users navigate your app by providing visual continuity. You can customize the color palette,
-the touch feedback animations, and the activity transitions for your app.</p>
+<p>The system widgets have a new design and touch feedback animations. You can customize the
+color palette, the touch feedback animations, and the activity transitions for your app.</p>
 
 <p>The material theme is defined as:</p>
 
@@ -43,7 +43,7 @@
 </ul>
 
 <p>For a list of material styles that you can use, see the API reference for
-<code>android.R.styles</code>.</p>
+<code>android.R.style</code>.</p>
 
 <p class="note">
 <strong>Note:</strong> The material theme is only available in the Android L Developer Preview.
@@ -53,8 +53,8 @@
 
 <h2 id="colorpalette">Customize the Color Palette</h2>
 
-<p>To customize the theme's base colors to fit your brand, define your custom colors using
-theme attributes when you inherit from the material theme:</p>
+<p style="margin-bottom:30px">To customize the theme's base colors to fit your brand, define
+your custom colors using theme attributes when you inherit from the material theme:</p>
 
 <pre>
 &lt;resources>
@@ -63,28 +63,27 @@
     &lt;!-- Main theme colors -->
     &lt;!--   your app's branding color (for the app bar) -->
     &lt;item name="android:colorPrimary">@color/primary&lt;/item>
-    &lt;!--   darker variant of colorPrimary (for contextual app bars) -->
+    &lt;!--   darker variant of colorPrimary (for status bar, contextual app bars) -->
     &lt;item name="android:colorPrimaryDark">@color/primary_dark&lt;/item>
-
-    &lt;!-- other theme colors -->
-    &lt;item name="android:colorButtonNormal">@color/button_normal&lt;/item>
-    &lt;item name="android:windowBackground">@color/wbackground&lt;/item>
+    &lt;!--   theme UI controls like checkboxes and text fields -->
+    &lt;item name="android:colorAccent">@color/accent&lt;/item>
   &lt;/style>
 &lt;/resources>
 </pre>
 
 
-<h2 id="statusbar">Customize the Status Bar</h2>
+<h2 id="statusbar">Customize the Status and Navigation Bar</h2>
 
 <p>The material theme lets you easily customize the status bar, so you can specify a
-color which fits your brand and provides enough contrast to show the white status icons. To
+color that fits your brand and provides enough contrast to show the white status icons. To
 set a custom color for the status bar, use the <code>android:statusBarColor</code> attribute when
-you extend the material theme.</p>
+you extend the material theme. By default, <code>android:statusBarColor</code> inherits the
+value of <code>android:colorPrimaryDark</code>.</p>
 
 <p>To handle the color of the status bar yourself (for example, by adding a gradient in the
 background), set the <code>android:statusBarColor</code> attribute to
-<code>&#64;android:color/transparent</code>. You can also use the
-<code>Window.setStatusBarColor</code> method for animations or fading.</p>
+<code>&#64;android:color/transparent</code> and adjust the window flags as required. You can
+also use the <code>Window.setStatusBarColor</code> method for animations or fading.</p>
 
 <p class="note"><strong>Note:</strong>
 The status bar should almost always have a clear delineation from the primary toolbar, except for
@@ -92,9 +91,9 @@
 </p>
 
 
-<h2 id="inheritance">Theme Inheritance</h3>
+<h2 id="inheritance">Theme Individual Views</h3>
 
-<p>In the Android L Developer Preview, elements in XML layout definitions can specify the
-<code>android:theme</code> attribute, which references a theme resource. This attribute modifies
-the theme for the element and any elements inflated below it, which is useful to alter theme
-color palettes in a specific portion of an interface.</p>
\ No newline at end of file
+<p>Elements in XML layout definitions can specify the <code>android:theme</code> attribute,
+which references a theme resource. This attribute modifies the theme for the element and any
+elements inflated below it, which is useful to alter theme color palettes in a specific portion
+of an interface.</p>
\ No newline at end of file
diff --git a/docs/html/preview/material/ui-widgets.jd b/docs/html/preview/material/ui-widgets.jd
index f18bff9..31604d6 100644
--- a/docs/html/preview/material/ui-widgets.jd
+++ b/docs/html/preview/material/ui-widgets.jd
@@ -14,19 +14,20 @@
 
 <p>The support library in the Android L Developer Preview contains two new widgets,
 <code>RecyclerView</code> and <code>CardView</code>. Use these widgets to show complex lists
-and cards in your app. These widgets have material design styles and animations by default.</p>
+and cards in your app. These widgets have material design style by default.</p>
 
 
 <h2 id="recyclerview">RecyclerView</h2>
 
-<p><code>RecyclerView</code> is a more advanced version of <code>ListView</code>. This widget is
-a container for large sets of views that can be recycled and scrolled very efficiently. Use the
-<code>RecyclerView</code> widget when you have lists with elements that change dynamically.</p>
+<p><code>RecyclerView</code> is a more advanced and flexible version of <code>ListView</code>.
+This widget is a container for large sets of views that can be recycled and scrolled very
+efficiently. Use the <code>RecyclerView</code> widget when you have lists with elements that
+change dynamically.</p>
 
 <p><code>RecyclerView</code> is easy to use, because it provides:</p>
 
 <ul>
-  <li>A set of layout managers for positioning items</li>
+  <li>A layout manager for positioning items</li>
   <li>Default animations for common item operations</li>
 </ul>
 
@@ -34,20 +35,9 @@
 widget.</p>
 
 <p>To use the <code>RecyclerView</code> widget, you have to specify an adapter and a layout
-manager. An <strong>adapter</strong> provides a binding from a dataset to views that are displayed
-within a <code>RecyclerView</code>. For example, if your dataset is an array of strings displayed
-as <code>TextView</code> items, the layout manager asks the adapter to:
-</p>
-
-<ul>
-  <li>Set the text of an existing <code>TextView</code> to one of the strings in the dataset</li>
-  <li>Create new <code>TextView</code> objects</li>
-  <li>Determine the size of the dataset</li>
-</ul>
-
-<p>To create an adapter, you extend the <code>RecyclerView.Adapter</code> class. The details of
-the implementation depend on the specifics of your dataset and the type of views. Fore more
-information, see the examples below.</p>
+manager. To create an adapter, you extend the <code>RecyclerView.Adapter</code> class. The details
+of the implementation depend on the specifics of your dataset and the type of views. For more
+information, see the <a href="#rvexamples">examples</a> below.</p>
 
 <img src="/preview/material/images/RecyclerView.png" alt="" id="figure1" style="width:550px"/>
 <p class="img-caption">
@@ -62,16 +52,17 @@
 <code>findViewById</code> lookups.
 </p>
 
-<p><code>RecyclerView</code> provides two layout managers you can use:</p>
+<p><code>RecyclerView</code> provides <code>LinearLayoutManager</code>, which shows the items in a
+vertical or horizontal scrolling list. To create a custom layout, you extend the
+<code>RecyclerView.LayoutManager</code> class.</p>
 
-<ul>
-  <li><code>LinearLayoutManager</code> shows the items in a vertically scrolling list.</li>
-  <li><code>GridLayoutManager</code> shows the items in a rectangular grid.</li>
-</ul>
+<h3>Animations</h3>
 
-<p>To create a custom layout, you extend the <code>RecyclerView.LayoutManager</code> class.</p>
+<p>Animations for adding and removing items are enabled by default in <code>RecyclerView</code>.
+To customize these animations, extend the <code>RecyclerView.ItemAnimator</code> class and use
+the <code>RecyclerView.setItemAnimator</code> method.</p>
 
-<h3>Examples</h3>
+<h3 id="rvexamples">Examples</h3>
 
 <p>To include a <code>RecyclerView</code> in your layout:</p>
 
@@ -87,7 +78,7 @@
 <p>To get the <code>RecyclerView</code> object in your activity:</p>
 
 <pre>
-public class MyActivity extends ActionBarActivity {
+public class MyActivity extends Activity {
     private RecyclerView mRecyclerView;
     private RecyclerView.Adapter mAdapter;
     private RecyclerView.LayoutManager mLayoutManager;
@@ -98,7 +89,8 @@
         setContentView(R.layout.my_activity);
         mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
 
-        // improve performance if the size is fixed
+        // improve performance if you know that changes in content
+        // do not change the size of the RecyclerView
         mRecyclerView.setHasFixedSize(true);
 
         // use a linear layout manager
@@ -139,7 +131,8 @@
     public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                    int viewType) {
         // create a new view
-        View v = new TextView(parent.getContext());
+        View v = LayoutInflater.from(parent.getContext())
+                               .inflate(R.layout.my_text_view, null);
         // set the view's size, margins, paddings and layout parameters
         ...
         ViewHolder vh = new ViewHolder(v);
@@ -167,22 +160,30 @@
 <h2 id="cardview">CardView</h2>
 
 <p><code>CardView</code> extends the <code>FrameLayout</code> class and lets you show information
-inside a card with optional rounded corners:</p>
+inside cards that have a consistent look on any app. <code>CardView</code> widgets can have
+shadows and rounded corners.</p>
+
+<p>To create a card with a shadow, use the <code>android:elevation</code> attribute.
+<code>CardView</code> uses real elevation and dynamic shadows
+and falls back to a programmatic shadow implementation on earlier versions. For more information,
+see <a href="{@docRoot}preview/material/compatibility.html">Compatibility</a>.</p>
+
+<p>Here's how to specify properties of <code>CardView</code>:</p>
 
 <ul>
   <li>To set the corner radius in your layouts, use the <code>android:cardCornerRadius</code>
   attribute.</li>
   <li>To set the corner radius in your code, use the <code>CardView.setRadius</code> method.</li>
+  <li>To set the background color of a card, use the <code>android:cardBackgroundColor</code>
+attribute.</li>
 </ul>
 
-<p>To set the background color of a card, use the <code>android:cardBackgroundColor</code>
-attribute.</p>
-
 <p>To include a <code>CardView</code> in your layout:</p>
 
 <pre>
 &lt;!-- A CardView that contains a TextView -->
 &lt;android.support.v7.widget.CardView
+    xmlns:card_view="http://schemas.android.com/apk/res-auto"
     android:id="@+id/card_view"
     android:layout_gravity="center"
     android:layout_width="200dp"
diff --git a/docs/html/preview/material/views-shadows.jd b/docs/html/preview/material/views-shadows.jd
index c5884d6..f7682f5 100644
--- a/docs/html/preview/material/views-shadows.jd
+++ b/docs/html/preview/material/views-shadows.jd
@@ -13,10 +13,12 @@
 </div>
 </div>
 
-<p>In apps with material design, depth has meaning. You should assign higher elevation values to more
-important UI elements in your app. The elevation value of a view determines the size of its
-shadow: views with higher Z values cast bigger shadows. Views only cast shadows on the Z=0 plane
-under an orthographic projection (the views do not scale for different values of Z).</p>
+<p>The elevation of a view determines the size of its shadow:
+views with higher Z values cast bigger shadows. Views only cast shadows on the Z=0 plane under an
+orthographic projection (the views do not scale for different values of Z).</p>
+
+<p>Elevation is also useful to create animations where widgets temporarily rise above the
+view plane when performing some action.</p>
 
 
 <h2 id="elevation">View Elevation</h2>
@@ -35,52 +37,59 @@
 
 <p>To set the translation of a view, use the <code>View.setTranslationZ</code> method.</p>
 
-<p>The Z values are measured in the same units as the X and Y values (like <code>dp</code> or
-<code>px</code>).</p>
+<p>The new <code>ViewPropertyAnimator.z</code> and <code>ViewPropertyAnimator.translationZ</code>
+methods enable you to easily animate the elevation of views. For more information, see
+the API reference for <code>ViewPropertyAnimator</code> and the <a
+href="{@docRoot}guide/topics/graphics/prop-animation.html#object-animator">Property Animation</a>
+developer guide.</p>
+
+<p>The Z values are measured in the same units as the X and Y values.</p>
 
 
 <h2 id="shadows">Shadows and Outlines</h2>
 
-<p>The bounds of a view's background drawable determine the default shape of its shadow. To define
-a custom shape for a shadow, such as an oval, use the <code>View.setOutline</code> method:</p>
+<p>The bounds of a view's background drawable determine the default shape of its shadow.
+<strong>Outlines</strong> represent the outer shape of a graphics object and define the ripple
+area for touch feedback.</p>
+
+<p>For example, if you define a view with a background drawable:</p>
 
 <pre>
-View v = findViewById(R.id.my_view);
-
-// add 10px to the static elevation
-v.setTranslationZ(10);
-
-// set an oval shadow
-Outline outline = new Outline();
-outline.setOval(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
-myView.setOutline(outline);
+&lt;TextView
+    android:id="@+id/myview"
+    ...
+    android:elevation="2dp"
+    android:background="@drawable/myrect" />
 </pre>
 
-<p>An <code>Outline</code> represents the outer shape of a graphics object. You can create
-<code>Outline</code> objects as in this example, or you can obtain the outline from a
-<code>Drawable</code> object with the <code>getOutline</code> method.</p>
+<p>where the background drawable is defined as a rectangle with rounded corners:</p>
 
-<p>The outline of a view also defines the ripple area for touch feedback.</p>
+<pre>
+&lt;!-- res/drawable/myrect.xml -->
+&lt;shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+    &lt;solid android:color="#42000000" />
+    &lt;corners android:radius="5dp" />
+&lt;/shape>
+</pre>
+
+<p>Then this view and drawable cast the appropiate shadow.</p>
+
+<p>You can also create outlines in your code using the methods in the <code>Outline</code> class,
+and you can assign them to views with the <code>View.setOutline</code> method.</p>
 
 <p>To prevent a view from casting a shadow, set its outline to <code>null</code>.</p>
 
 
 <h2 id="clip">Clipping Views</h2>
 
-<p>The Android L Developer Preview lets you clip a view to its outline area using the
+<p>Clip a view to its outline area using the
 <code>View.setClipToOutline</code> method. Only rectangle, circle, and round rectangle outlines
 support clipping, as determined by the <code>Outline.canClip</code> method.</p>
 
-<p>To determine if a view has been clipped, use the <code>View.getClipToOutline</code> method.</p>
+<p>To clip a view to the shape of a drawable, set the drawable as the background of the view
+(as shown above) and call the <code>View.setClipToOutline</code> method.</p>
 
-<pre>
-// clip a view to an oval
-View v = findViewById(R.id.my_view);
-outline.setOval(v.getLeft(), v.getTop(), v.getRight(), v.getBottom());
-myView.setOutline(outline);
-
-// if the view is not already clipped
-if (v.getClipToOutline() == false) {
-    v.setClipToOutline(true);
-}
-</pre>
\ No newline at end of file
+<p>Because clipping views is an expensive operation, don't animate the shape you use to
+clip a view. To achieve this effect, use a <a
+href="{@docRoot}preview/material/animations.html#reveal">Reveal Effect</a> animation.</p>
\ No newline at end of file
diff --git a/docs/html/preview/preview_toc.cs b/docs/html/preview/preview_toc.cs
index 5920ecc..75703a8 100644
--- a/docs/html/preview/preview_toc.cs
+++ b/docs/html/preview/preview_toc.cs
@@ -2,11 +2,7 @@
 
 
   <li class="nav-section">
-    <div class="nav-section-header empty"><a href="<?cs var:toroot ?>preview/setup-sdk.html">Set up the Preview SDK
-      </a></div>
-  </li>
-  <li class="nav-section">
-    <div class="nav-section-header empty"><a href="<?cs var:toroot ?>preview/setup-devices.html">Set Up Hardware and AVDs
+    <div class="nav-section-header empty"><a href="<?cs var:toroot ?>preview/setup-sdk.html">Set up the SDK
       </a></div>
   </li>
   <li class="nav-section">
@@ -70,7 +66,7 @@
   <li class="nav-section">
     <div class="nav-section-header empty">
 
-      <a href="<?cs var:toroot ?>preview/l-developer-preview-reference.zip">Reference</a>
+      <a href="<?cs var:toroot ?>preview/reference.html">Reference</a>
 
     </div>
   </li>
@@ -81,7 +77,12 @@
   </li>
   <li class="nav-section">
     <div class="nav-section-header empty">
-      <a href="<?cs var:toroot ?>preview/tos.html">Terms of Service</a>
+      <a href="<?cs var:toroot ?>preview/license.html">License Agreement</a>
+      </div>
+  </li>
+  <li class="nav-section" style="margin: 20px 0 0 3px;">
+    <div class="nav-section-header paging-links empty">
+      <a href="<?cs var:toroot ?>index.html" class="prev-page-link">Developer Home</a>
       </div>
   </li>
 </ul>
diff --git a/docs/html/preview/reference.jd b/docs/html/preview/reference.jd
new file mode 100644
index 0000000..f70f7a2
--- /dev/null
+++ b/docs/html/preview/reference.jd
@@ -0,0 +1,13 @@
+page.title=Reference
+
+@jd:body
+
+<p>The reference documentation and API difference report are available as downloadable packages.
+</p>
+
+<ul>
+  <li><a href="{@docRoot}preview/l-developer-preview-reference.zip">L
+  Developer Preview reference</a></li>
+  <li><a href="{@docRoot}preview/l-developer-preview-api-diff.zip">L
+  Developer Preview difference report</a></li>
+</ul>
\ No newline at end of file
diff --git a/docs/html/preview/samples.jd b/docs/html/preview/samples.jd
index 67404b6..9bccb31 100644
--- a/docs/html/preview/samples.jd
+++ b/docs/html/preview/samples.jd
@@ -2,15 +2,4 @@
 
 @jd:body
 
-<p>The code samples for the L Developer Preview are available in the Android SDK Manager under the
-L Preview section. Here is a summary of everything that is available:</p>
-
-<ul>
-  <li></li>
-  <li></li>
-  <li></li>
-  <li></li>
-  <li></li>
-  <li></li>
-  <li></li>
- </ul>
+<p>&nbsp;</p>
\ No newline at end of file
diff --git a/docs/html/preview/setup-devices.jd b/docs/html/preview/setup-devices.jd
deleted file mode 100644
index 86e4845..0000000
--- a/docs/html/preview/setup-devices.jd
+++ /dev/null
@@ -1,90 +0,0 @@
-page.title=Setting Up Hardware and AVDs
-@jd:body
-
-<p>The Android L developer preview provides you with 32-bit system images
-to flash the following devices:
-</p>
-
-<ul>
-  <li>Nexus 5</li>
-  <li>Nexus 7 Wi-Fi (version 2, released in 2013)</li>
-</ul>
-
-<p>In addition, you also get the emulator system images, which includes
-experimental 64-bit system images along with standard 32-bit system images.
-</p>
-
-<h2>Installing the L Preview System Image</h2>
-
-<!-- Will we get an official warning text from the lawyercats? Is this it? -->
-<p class="warning"><b>Warning</b>: This is a preview version of the Android
-system image, and is subject to change. Your use of this system image is
-governed by the Android SDK Preview License Agreement. The Android preview
-system image is not a stable release, and may contain errors and defects that
-can result in damage to your computer systems, devices, and data. The preview
-Android system image is not subject to the same testing as the factory OS and
-can cause your phone and installed services and applications to stop working.
-</p>
-
-
-<ol>
-  <li>Download and extract the Android Developer Preview package to a directory
-  (which we'll call <code>&lt;l_download_dir&gt;</code> in these
-  instructions).</li>
-  <li>Connect your powered-off Android device to your development machine. Put
-  the device in fastboot mode by pressing and holding the following buttons:
-    <ul>
-    <li><strong>Nexus 5:</strong> <i>volume down</i> + <i>volume up</i> +
-        <i>power</i></li>
-    <li><strong>Nexus 7:</strong> <i>volume down</i> + <i>power</i> </li>
-    </ul>
-    <p class="note">Alternatively, you can enter fastboot mode by booting up
-    the device and running <code>adb reboot bootloader</code> with USB debugging
-    turned on.</p>
-  </li>
-  <li>Follow the instructions at
-  <a href="https://developers.google.com/android/nexus/images#instructions">developers.google.com/android</a>
-  to set up your system for flashing devices.</li>
-  <li>Run the <code>&lt;l_download_dir&gt;/flash-all</code> script
-  corresponding to your platform. This script flashes all of the system data
-  onto the phone.</li> <!-- Confirm names of flash scripts -->
-  <li>(Optional) After flashing is complete, lock your device's bootloader by
-  putting it in   fastboot mode and running <code>fastboot oem lock</code>.
-  (This does not wipe   your device.) Once you do this,  you will not be able to
-  flash your device until you run   run <code>fastboot oem   unlock</code>,
-  which unlocks the bootloader and wipes your device. We recommend you leave the
-  bootloader unlocked until you are done with flashing the device.</li>
-</ol>
-
-<h3>Reverting a Device to Factory Specifications</h3>
-
-  <p>If you want to uninstall the L Preview and revert the device to factory
-specifications, go to <a href="http://developers.google.com/android
-/nexus/images">developers.google.com/android</a> and download the image you want
-to flash to for your device. Follow the instructions on that page to flash the
-image to your device.</p>
-
-
-<h2>Setting up an AVD</h2>
-
-<p>You can set up <a href="{@docRoot}tools/devices/">Android Virtual Devices
-(AVD)</a> and use the emulator to build and test apps with the L Preview.</p>
-
-<p>To create an AVD with the AVD Manager:</p>
-
-<ol>
-  <li>Install the L Preview SDK in your development environment, as described
-      in <a href="{@docRoot}preview/setup-sdk.html">Setting Up the Preview
-      SDK.</a></li>
-  <li>Follow the steps in
-      <a href="{@docRoot}tools/devices/managing-avds.html">Managing AVDs with AVD
-      Manager</a>. Use the following settings:
-    <ul>
-      <li><b>Device:</b> Either Nexus 5 or Nexus 7</li>
-      <li><b>Target:</b> <!-- Confirm exact text when we have final distro -->
-       Android L (Preview) - API Level L</li>
-    </ul>
-    <!-- Confirm this works when you can download image through SDK manager! -->
-  </li>
-</ol>
-
diff --git a/docs/html/preview/setup-sdk.jd b/docs/html/preview/setup-sdk.jd
index 32a33b6..876b348 100644
--- a/docs/html/preview/setup-sdk.jd
+++ b/docs/html/preview/setup-sdk.jd
@@ -7,7 +7,7 @@
 Android, see <a href="/training/basics/firstapp/index.html">Building Your First
 App</a> training lesson first.</a></p>
 
-<h2>Download the SDK</h2>
+<h2 id="downloadSdk">Download the SDK</h2>
 
 <ol>
   <li>Start the Android SDK Manager.</li>
@@ -20,17 +20,125 @@
     <b>Install</b>.</li>
 </ol>
 
-<h2>Set up your environment</h2>
+<p class="note"><strong>Note:</strong> The Eclipse ADT plug-in requires Java 7
+if your compilation target is the L developer preview.</p>
+
+<h2 id="setupHardware">Set Up Hardware and AVDs</h2>
+
+<p>The Android L developer preview provides you with 32-bit system images
+to flash the following devices:
+</p>
+
+<ul>
+  <li>Nexus 5</li>
+  <li>Nexus 7 Wi-Fi (version 2, released in 2013)</li>
+</ul>
+
+<p>In addition, you also get the emulator system images, which includes
+experimental 64-bit system images along with standard 32-bit system images.
+</p>
+
+<h3 id="installImage">Install the L Preview System Image</h3>
+
+<p class="warning"><b>Warning:</b> This is a preview version of the Android
+system image, and is subject to change. Your use of this system image is
+governed by the Android SDK Preview License Agreement. The Android preview
+system image is not a stable release, and may contain errors and defects that
+can result in damage to your computer systems, devices, and data. The preview
+Android system image is not subject to the same testing as the factory OS and
+can cause your phone and installed services and applications to stop working.
+</p>
+
 
 <ol>
-  <li>Create a new Android project with the following properties:
-    <ul>
-      <li>Minimum SDK Version: L</li>
-      <li>Target SDK Version: L</li>
-      <li>Build Target: L</li>
-    </ul>
+  <li>Download and uncompress the Android Developer Preview package.
+    <p class="table-caption" id="table1">
+      <strong>Table 1.</strong> L Developer Preview system images.</p>
+    <table>
+      <tr>
+        <th scope="col">Device</th>
+        <th scope="col">Download</th>
+        <th scope="col">MD5 Checksum</th>
+        <th scope="col">SHA-1 Checksum</th>
+      </tr>
+      <tr id="hammerhead">
+        <td>Nexus 5 (GSM/LTE) "hammerhead"</td>
+        <td><!-- TODO --></td>
+        <td><code>5a6ae77217978cb7b958a240c2e80b57</code></td>
+        <td><code>ac1d8a8e4f4a1dca5864dc733caa940bffc28616</code></td>
+      </tr>
+      <tr id="razor">
+        <td>Nexus 7 (Wifi) "razor"</td>
+        <td><!-- TODO --></td>
+        <td><code>b293a5d3a4e07beabebcc0be85ad68a2</code></td>
+        <td><code>d0ddf8ce733ba2a34279cdff8827fd604762c2342d</code></td>
+      </tr>
+    </table>
   </li>
-  <li>Choose the theme <code>Theme.Material</code>
-    <!-- put in name as it appears in Eclipse menu? -->
 
+  <li>Follow the instructions at
+  <a href="https://developers.google.com/android/nexus/images#instructions">developers.google.com/android</a>
+  to flash the image onto your device.</li>
 </ol>
+
+<h3 id="revertDevice">Revert a Device to Factory Specifications</h3>
+
+  <p>If you want to uninstall the L Preview and revert the device to factory
+specifications, go to <a href="http://developers.google.com/android
+/nexus/images">developers.google.com/android</a> and download the image you want
+to flash to for your device. Follow the instructions on that page to flash the
+image to your device.</p>
+
+<h3 id="setupAVD">Set up an AVD</h3>
+
+<p>You can set up <a href="{@docRoot}tools/devices/">Android Virtual Devices
+(AVD)</a> and use the emulator to build and test apps with the L Preview.</p>
+
+<p>To create an AVD with the AVD Manager:</p>
+
+<ol>
+  <li>Install the L Preview SDK in your development environment, as described
+      in <a href="{@docRoot}preview/setup-sdk.html">Setting Up the Preview
+      SDK.</a></li>
+  <li>Follow the steps in
+      <a href="{@docRoot}tools/devices/managing-avds.html">Managing AVDs with AVD
+      Manager</a>. Use the following settings:
+    <ul>
+      <li><b>Device:</b> Either Nexus 5 or Nexus 7</li>
+      <li><b>Target:</b> <!-- Confirm exact text when we have final distro -->
+       Android L (Preview) - API Level L</li>
+    </ul>
+    <!-- Confirm this works when you can download image through SDK manager! -->
+  </li>
+</ol>
+
+<h2 id="createProject">Create a Project</h2>
+
+<p>Android Studio makes it easy to create a project for the L Developer Preview. Follow
+the steps described in <a href="{@docRoot}sdk/installing/create-project.html">Creating a
+Project</a>. In the <strong>Form Factors</strong> screen:</p>
+
+<ul>
+  <li>Check <strong>Phone and Tablet</strong>.</li>
+  <li>Select <strong>API 20+: Android L (Preview)</strong> in <strong>Minimum SDK</strong>.</li>
+</ul>
+
+<p>On the development environment, open the <code>build.gradle</code> file for your module
+and make sure that:</p>
+
+<ul>
+  <li><code>compileSdkVersion</code> is set to <code>'android-L'</code></li>
+  <li><code>minSdkVersion</code> is set to <code>'L'</code></li>
+  <li><code>targetSdkVersion</code> is set to <code>'L'</code></li>
+</ul>
+
+<p>To use the material theme, open the <code>values/styles.xml</code> in your project and make
+sure that you theme extends the material theme:</p>
+
+<pre>
+&lt;resources>
+    &lt;style name="AppTheme" parent="android:Theme.Material">
+        &lt;!-- Customize your theme here -->
+    &lt;/style>
+&lt;/resources>
+</pre>
diff --git a/docs/html/preview/support.jd b/docs/html/preview/support.jd
index 23ce6ff..8efc4bc 100644
--- a/docs/html/preview/support.jd
+++ b/docs/html/preview/support.jd
@@ -2,21 +2,107 @@
 
 @jd:body
 
-<p>If you've encountered bugs or have feedback about the L Developer Preview, create
-an issue on our bug tracker</p>
+<p>If you've encountered bugs or have feedback about the L Developer Preview,
+<a href="https://code.google.com/p/android-developer-preview/">create an issue</a> on
+our issue tracker.</p>
 
-<p>Go to the Bug Tracker</p>
+<p>For more support,
+<a href="https://plus.google.com/communities/113159138894928487684">join
+the L Developer Preview Google+ community</a> to discuss your development experiences.
 
-<h2>Release Notes</h2>
 
+<h2 id="ReleaseNotes">Release Notes</h2>
 <p>June 25, 2014 - Initial Release of the L Developer Preview</p>
 
+<h3 id="UserInterface">User interface</h3>
 <ul>
-  <li></li>
-  <li></li>
-  <li></li>
-  <li></li>
-  <li></li>
-  <li></li>
-  <li></li>
- </ul>
+<li>If your app launches an activity with
+{@link android.app.Activity#startActivity startActivity()}
+and an {@link android.content.Intent} set to
+{@link android.content.Intent#FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET}, the
+activity shows up as a separate task in the Recent apps screen. This is the
+same behavior as though your app used {@code Intent.FLAG_ACTIVITY_NEW_DOCUMENT}
+(see <a href="{@docRoot}preview/api-overview.html#Recents">Concurrent
+documents and activities in the Recents screen</a>). If you want your activity
+to remain in the same task that launched it, use
+{@link android.app.Activity#startActivityForResult
+startActivityForResult()} instead.</li>
+<li>System-rendered shadows for user interface (UI) elements in views may
+appear with visible spiky edges. To avoid this visual artifact, use a higher
+<a href="{@docRoot}preview/material/views-shadows.html#elevation">view
+elevation</a>.</li>
+<li>On very tall or wide views, view shadows may appear with additional rough
+visual artifacts around the view edges. To minimize this, avoid using view
+shadows with very narrow views.</li>
+<li>The {@code android.graphics.drawable.RippleDrawable} class does not
+respond to pointer location changes, except when the drawable is set as a
+{@link android.view.View} background.</li>
+</ul>
+
+<h3 id="Multimedia">Multimedia</h3>
+<ul>
+<li>The {@code android.hardware.camera2} APIs are supported only on Nexus 5
+devices.</li>
+<li>Saving a DNG file with the new {@code android.hardware.camera2.DngCreator}
+API fails if lens shading compensation map generation is not enabled. To
+capture images to DNG files, add the following code when creating your capture
+requests:
+<pre>
+CaptureRequest.Builder stillCaptureRequest =
+        mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
+
+stillCaptureRequest.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
+        CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE_ON);
+</pre></li>
+<li>The {@code android.media.AudioTrack.write(float[], int, int, int)} method
+currently does not work. Use the
+{@link android.media.AudioTrack#write(short[], int, int)
+AudioTrack.write(short[], int, int)} method instead.</li>
+<li>Lockscreen security is currently not enforced when users start a Android
+mirroring session from the Quick Settings shade.</li>
+</ul>
+
+<h3 id="UserInput">User input</h3>
+<ul><li>The System UI may crash unexpectedly while the device is charging, if
+	the locale is set to {@code fr} (FRENCH).</li></ul>
+
+<h3 id="Wireless">Wireless and Connectivity</h3>
+<ul>
+<li>The {@code android.bluetooth.le} APIs are supported only on Nexus 5
+devices.</li>
+<li>You might encounter these issues while using Bluetooth LE scanning:
+	<ul>
+	<li><em>Settings</em> does not show all Bluetooth LE devices when a scan
+		filter is set.</li>
+	<li>System returns non-intuitive error messages during a Bluetooth LE scan,
+		when Bluetooth is off.</li>
+	<li>The {@code BluetoothLeScanner.startScan()} method starts failing after
+		six concurrent scans with different callbacks.</li>
+	</ul>
+</li>
+<li>You might encounter these issues while using Bluetooth LE advertising:
+	<ul>
+		<li>The device MAC address does not change for multiple advertising
+		when the application processor is asleep.</li>
+		<li>The TX Power Level is always 0 in advertising packets.</li>
+	</ul>
+</li>
+</ul>
+
+<h3 id="Enterprise">Enterprise</h3>
+<ul>
+<li>The device may crash unexpectedly in these situations when using
+Android work functionality:
+<ul>
+<li>The user attempts to share a web page (via <strong>Menu &gt; Share</strong>)
+from a non-Android work Chrome app to a Android work profile Gmail app.</li>
+<li>The user attempts to share a web page via Bluetooth from a
+Android work profile
+Chrome app.</li>
+<li>The user attempts to share a web page via Android Beam from a
+Android work profile Chrome app.</li>
+</ul>
+</li>
+<li>Deleting a Android work profile may take several minutes to complete. You
+cannot create a new Android work profile until the deletion operation is over.</li>
+</ul>
diff --git a/docs/html/preview/tos.jd b/docs/html/preview/tos.jd
deleted file mode 100644
index 602439f..0000000
--- a/docs/html/preview/tos.jd
+++ /dev/null
@@ -1,9 +0,0 @@
-page.title=License Agreement
-
-@jd:body
-
-<p><!-- Will this link change before we publish (to a clean version of the doc)?
-  Or will we scrub the doc's comments & revision history? -->
-<a href="https://docs.google.com/a/google.com/document/d/1OixnM1Q890ExOzDB3Z-FDD6Sb2kF4uZQiMxsYVII8F0/edit?usp=sharing">L
-Preview Terms of Service</a>
-</p>
\ No newline at end of file
diff --git a/docs/html/preview/tv/adt-1/index.jd b/docs/html/preview/tv/adt-1/index.jd
new file mode 100644
index 0000000..d83dd11
--- /dev/null
+++ b/docs/html/preview/tv/adt-1/index.jd
@@ -0,0 +1,282 @@
+page.title=ADT-1 Developer Kit
+page.tags="emote","e-mote","adt"
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#faq">Frequently Asked Questions</a>
+      <ol>
+        <li><a href="#setup">Device Setup</a></li>
+        <li><a href="#input">User Input</a></li>
+        <li><a href="#cast">Google Cast</a></li>
+        <li><a href="#trouble">Troubleshooting</a></li>
+      </ol>
+    </li>
+    <li><a href="#emote">Android TV Remote Control App</a></li>
+    <li><a href="#reg-safety">Regulatory Disclosures and Safety</a></li>
+  </ol>
+</div>
+</div>
+
+<p>The ADT-1 Developer Kit is a streaming media player and game controller designed for running
+and testing apps built for Android TV. Supplies of ADT-1 are limited and it is intended for
+developers who are interested in building new apps or extending their existing apps to run on the
+Android TV platform.</p>
+
+<p class="note">
+  <strong>Note:</strong> The ADT-1 kit <em>is not required</em> for building and testing apps
+  for Android TV. You can build apps for TV and test them using an emulator for TV devices. The
+  L Developer Preview includes all the software needed to build TV apps and an emulator for running
+  and testing them. For more information, see the
+  <a href="{@docRoot}preview/tv/start/index.html">Get Started</a> guide for TV apps.
+</p>
+
+<h2 id="faq">ADT-1 Frequently Asked Questions</h2>
+
+<p>The following information is provided to help set up and use the ADT-1 device.</p>
+
+
+<h3 id="setup">Device Setup</h3>
+
+<p>
+  <strong>How do I turn my device on?</strong>
+</p>
+<p>Plug the included power cable into the back of ADT-1. The device does not have an on/off
+  switch.</p>
+
+<p>
+  <strong>How do I completely turn my device off? </strong>
+</p>
+<p>Unplug the included power cable from the back of ADT-1. The device does not have an on/off
+  switch. However, ADT-1 will begin sleeping (daydream) based on user settings in
+  <strong>Settings &gt; Display &gt; Daydream</strong>.
+  </p>
+
+<p>
+  <strong>How do I connect to the network?</strong>
+</p>
+<p>ADT-1 has both wireless and Ethernet for connecting to your network. To change your wireless
+  network, go to <strong>Settings -&gt; Wi-Fi</strong>. To use an Ethernet network connection,
+  simply plug an Ethernet cable (that is connected to your network) into the port on the back of
+  ADT-1.</p>
+
+<p>
+  <strong>How do I use the developer cable?</strong>
+</p>
+<p>The developer cable has three connectors: a small, male power connector that plugs into the
+  power port on the back of ADT-1, a standard male USB-A connector that connects your PC, and a
+  small, female power connector that the included power supply plugs into.</p>
+
+
+
+<h3 id="input">User Input</h3>
+
+<p>
+  <strong>How do I put the gamepad that came with my ADT-1 into pairing mode?</strong>
+</p>
+<p>Press and hold the Back and Home buttons together for about three seconds, until all four
+  blue LEDs flash together. When the LEDs are flashing, the gamepad is in pairing mode.</p>
+
+<p>
+  <strong>How do I use the gamepad with the on-screen keyboard?</strong>
+</p>
+<p>Use the D-pad or left joystick to move the cursor, and press A to select. Press X to delete a
+  character, and press Y to insert a space. Also, you can press the right joystick to toggle caps
+  lock, and press the left joystick to show additional symbols.</p>
+
+<p>
+  <strong>Can I control ADT-1 with my phone or tablet?</strong>
+</p>
+<p>Yes. In order to control the ADT-1 with Android phones or tablets, you can download a remote
+  control app from the Google Play store. For more information, see <a href="#emote">Android TV
+  Remote Control App</a>.
+  </p>
+
+<p>
+  <strong>Can I connect a USB keyboard or mouse to ADT-1?</strong>
+</p>
+<p>Yes, you can connect a USB keyboard or mouse to the USB port on the back of ADT-1.
+
+<p class="note">
+  <strong>Note:</strong> The ADT-1 device is not compatible with all manufacturers and models of
+  these devices. If a particular keyboard or mouse does not work, try a different model.
+</p>
+
+<p>
+  <strong>How do I connect a Bluetooth device without an input device already attached?</strong>
+</p>
+<p>You can put ADT-1 into Bluetooth pairing mode using a hardware button. Press the small, round
+  button on the back of ADT-1 to make it search for Bluetooth devices in pairing mode. If multiple
+  accessories are found, press the small, round button to select the device you want to pair.
+  Pairing will happen automatically after a few seconds.
+</p>
+
+<p>
+  <strong>How do I connect additional Bluetooth accessories?</strong>
+<p>
+<p>To pair Bluetooth devices to ADT-1 from the user interface, go to <strong>Settings &gt;
+  Remote &amp; Accessories &gt; Add accessory</strong>
+
+
+<h3 id="cast">Google Cast</h3>
+
+<p>
+  <strong>Can I cast to an ADT-1 device?</strong>
+<p>
+<p>Yes. The ADT-1 includes Google Cast receiver functionality, similar to Chromecast. Since the
+  ADT-1 is a developer device running a development software release, the Google Cast receiver is
+  open only to a limited number of apps.</p>
+
+<p>
+  <strong>Which Cast apps are supported on ADT-1?</strong>
+<p>
+<p>As a developer device, the ADT-1 supports casting from only the following apps/websites:</p>
+
+<ul>
+  <li>YouTube</li>
+  <li>Netflix</li>
+  <li>Google+ Photos</li>
+  <li>Google Play Movies and TV (Android only)</li>
+</ul>
+
+<p>Coming soon:</p>
+
+<ul>
+  <li>Google Play Music</li>
+  <li>Google Play Movies and TV (iOS and Chrome)</li>
+  <li>Mirror you Android device screen to ADT-1</li>
+</ul>
+
+<p class="note">
+  <strong>Note:</strong> When casting from a Chrome browser, you must use Chrome V.36 or higher.
+  Chrome V.36 is available in beta-channel and is planned to be released soon.
+</p>
+
+<p>
+  <strong>How do I cast to ADT-1?</strong>
+<p>
+<p>You cast to an ADT-1 device the same way you do with a Chromecast device. Open the supported
+  Cast apps or webpages, press the <strong>Cast</strong> button and you should see the ADT-1 as a
+  Cast target. For more infomation about on how to cast, see
+  <a href="http://www.google.com/intl/en/chrome/devices/chromecast/learn.html">Learn How to
+  Cast</a>.
+  </p>
+
+<p>
+  <strong>Will my Google Cast sender apps work on ADT-1 just like Chromecast?</strong>
+<p>
+<p>Yes. Your Cast app works on ADT-1 and Android TV products without additional work.<p>
+
+<p class="note">
+  <strong>Note:</strong> Your iOS sender app requires the Google Cast iOS API version 2.2.1
+  or later to work with the ADT-1 device.
+</p>
+
+<p>
+  <strong>How do I register my ADT-1 in order to run my apps?</strong>
+</p>
+<ol>
+  <li>Go to <strong>Settings &gt; Google Cast</strong> and turn on developer support, allowing the
+    ADT-1 device to send its serial number to Google.</li>
+  <li>Register your ADT-1 device in the Google Cast Developer Console, using the 12 character
+    serial number engraved on the back of the ADT-1.</li>
+</ol>
+
+<p>For more Google Cast developer information, see the
+  <a href="https://developers.google.com/cast/">Cast developer site</a>. Please use the Google Cast
+  SDK <a href="https://code.google.com/p/google-cast-sdk/issues/list">issue tracker</a> for filing
+  issues related to Cast. Make sure you mention the ADT-1 device when filing an issue.
+</p>
+
+<p>
+  <strong>How do I debug my Cast app on ADT-1?</strong>
+</p>
+<p>Connect your development platform using the power/USB cable, and using a Chrome browser,
+  navigate to <code>chrome://inspect/#devices</code> to debug the webview.</p>
+
+
+<h3 id="trouble">Troubleshooting</h3>
+
+<p>
+  <strong>Why doesn't the on-screen keyboard come up?</strong>
+</p>
+<p>Enable the keyboard in the device Settings. Go to <strong>Settings &gt; Keyboard &gt; Current
+  keyboard</strong> and choose <strong>Leanback keyboard</strong>.
+
+<p>
+  <strong>How do I perform a hardware reboot?</strong>
+</p>
+<p>Locked it up, huh? No worries. We've done that a few times ourselves. Unplug and replug the
+  included power cable from the back of ADT-1 to reboot it.
+</p>
+
+<p>
+  <strong>How do I perform a factory reset?</strong>
+</p>
+<p class="warning">
+  <strong>Warning:</strong> This procedure removes all data from the device, including system
+  data, downloaded apps, app data, and account settings.
+</p>
+
+<p>From the home screen, go to <strong>Settings &gt; Device &gt; Factory data reset</strong>, and
+  select <strong>Reset device</strong>.
+</p>
+
+<p>
+  <strong>How do I perform a hardware reset?</strong>
+</p>
+<p class="warning">
+  <strong>Warning:</strong> This procedure performs a factory data reset, removing all data from
+  the device, including system data, downloaded apps, app data, and account settings.
+</p>
+
+<p>Unplug the power cable from the back of ADT-1. Press and hold the small, round button on the
+  back of ADT-1 as you re-insert the power cable, and continue to hold the small round button. The
+  LED will begin flashing red for a few seconds, then change to multi-color cycle. When the LED
+  starts the multi-color cycle, release the small, round button, and ADT-1 boots up. If you release
+  the button while the LED is flashing red, the device will be in Fastboot mode.</p>
+
+<p>
+  <strong>There is a hardware problem with my ADT-1. How do I return it?</strong>
+</p>
+<p>You can request a return of the device using the
+  <a href="https://support.google.com/googleplay/android-developer/contact/adt_rma">return
+  merchandise authorization form</a>.
+</p>
+
+
+<h2 id="emote">Android TV Remote Control App</h2>
+
+<div class="figure" style="width:250px;margin-top:0">
+<img src="/preview/tv/images/android-tv-remote.png" alt="Android TV Remote Screenshots">
+</div>
+
+<p>A remote control app is available for Android phones and tablets that allows you to interact
+  with the ADT-1 device. This app allows you to switch between D-pad input mode or touchpad mode
+  to navigate content and play games on a Android TV device. You can also tap the mic button to
+  start a voice search, or use the keyboard to input text using this app.</p>
+
+<p>You download the remote control app from the Google Play store using
+  <a href="https://play.google.com/store/apps/details?id=com.google.android.tv.remote">this
+  link</a>.
+</p>
+
+<p class="note">
+  <strong>Note:</strong> your Android ​phone or tablet must be connected to the same local network
+  as ADT-1.
+</p>
+
+
+<h2 id="reg-safety">Regulatory Disclosures and Safety Information</h2>
+
+<p>The ADT-1 device comes with important regulatory disclosures and safety information. Please
+read this information before using the device:</p>
+
+<ul>
+  <li><a href="regulatory.html">Regulatory Disclosures</a></li>
+  <li><a href="safety.html">Important Safety Information</a></li>
+</ul>
+
diff --git a/docs/html/preview/tv/adt-1/regulatory.jd b/docs/html/preview/tv/adt-1/regulatory.jd
new file mode 100644
index 0000000..2f5bf7e
--- /dev/null
+++ b/docs/html/preview/tv/adt-1/regulatory.jd
@@ -0,0 +1,79 @@
+page.title=Regulatory Disclosures for ADT-1
+parent.title=ADT-1 Developer Kit
+parent.link=index.html
+
+@jd:body
+
+<p>Disclosures for the <a href="index.html">ADT-1</a> device.</p>
+
+<p>
+  Model: W2<br>
+  FCC ID: A4R-W2<br>
+  IC: 10395A-W2
+</p>
+
+<p>U.S. Federal Communications Commission Notices</p>
+<p>To satisfy FCC and IC exposure requirements, a separation distance of at least 20 cm should
+  be maintained between the antenna of this device and persons during device operation. Operations
+  at closer than this distance are not recommended.</p>
+<p>The antenna used for this transmitter must not be co-located in conjunction with any other
+  antenna or transmitter.</p>
+<p>This equipment has been tested and found to comply with the limits for a Class B digital
+  device, pursuant to part 15 of the FCC Rules. These limits are designed to provide reasonable
+  protection against harmful interference in a residential installation. This equipment generates,
+  uses and can radiate radio frequency energy and, if not installed and used in accordance with the
+  instructions, may cause harmful interference to radio communications. However, there is no
+  guarantee that interference will not occur in a particular installation. If this equipment does
+  cause harmful interference to radio or television reception, which can be determined by turning
+  the equipment off and on, the user is encouraged to try to correct the interference by one or more
+  of the following measures:</p>
+<p>—Reorient or relocate the receiving antenna.</p>
+<p>—Increase the separation between the equipment and receiver.</p>
+<p>—Connect the equipment into an outlet on a circuit different from that to which the receiver
+  is connected.</p>
+<p>—Consult the dealer or an experienced radio/ TV technician for help.</p>
+<p>This device complies with part 15 of the FCC Rules. Operation is subject to the following two
+  conditions: (1) This device may not cause harmful interference, and (2) this device must accept
+  any interference received, including interference that may cause undesired operation.</p>
+<p>Changes or modifications not expressly approved by Google Inc. could void the user's
+  authority to operate the equipment.</p>
+<p>Industry Canada Notices</p>
+<p>This device complies with Industry Canada licence-exempt RSS standard(s). Operation is
+  subject to the following two conditions: (1) this device may not cause interference, and (2) this
+  device must accept any interference, including interference that may cause undesired operation of
+  the device.</p>
+<p>Under Industry Canada regulations, this radio transmitter may only operate using an antenna
+  of a type and maximum (or lesser) gain approved for the transmitter by Industry Canada. To reduce
+  potential radio interference to other users, the antenna type and its gain should be so chosen
+  that the equivalent isotropically radiated power (e.i.r.p.) is not more than that necessary for
+  successful communication.</p>
+<p>The radiated output power of the Wireless Device is below the Industry Canada (IC) radio
+  frequency exposure limits. The Wireless Device should be used in such a manner such that the
+  potential for human contact during normal operation is minimized.</p>
+
+<hr />
+
+<p>CAN ICES-3 (B)/NMB-3(B)</p>
+<p>
+  <u>Avis d’<em>Industrie Canada</em></u>
+</p>
+<p>
+  Le présent appareil est conforme aux <em>CNR</em> d'Industrie Canada applicables aux appareils
+  radio exempts de licence. L'exploitation est autorisée aux deux conditions suivantes : (1)
+  l'appareil ne doit pas produire de brouillage, et (2) l'appareil doit accepter tout brouillage
+  radioélectrique subi, même si le brouillage est susceptible d'en compromettre le fonctionnement.
+</p>
+<p>
+  En vertu de la règlementation d’<em>Industrie Canada</em>, cet émetteur radio peut
+    fonctionner avec une antenne d'un type et d'un gain maximal (ou inférieur) approuvé pour
+    l'émetteur par <em>Industrie Canada</em>. Dans le but de réduire les risques de brouillage
+  radioélectrique à l'intention des autres utilisateurs, il faut choisir le type d'antenne et son
+  gain de sorte que la puissance isotrope rayonnée équivalente (p.i.r.e.) ne dépasse pas l'intensité
+  nécessaire à l'établissement d'une communication satisfaisante.
+</p>
+<p>
+  La puissance rayonnée en sortie de l'appareil sans fil est inférieure aux limites fixées par
+  <em>Industrie Canada</em> en matière d'exposition aux radiofréquences. L'appareil sans fil
+  doit être utilisé de sorte que la possibilité d'un contact humain pendant le fonctionnement
+  normal soit limitée.
+</p>
diff --git a/docs/html/preview/tv/adt-1/safety.jd b/docs/html/preview/tv/adt-1/safety.jd
new file mode 100644
index 0000000..1984853
--- /dev/null
+++ b/docs/html/preview/tv/adt-1/safety.jd
@@ -0,0 +1,140 @@
+page.title=Important Safety Instructions for ADT-1
+parent.title=ADT-1 Developer Kit
+parent.link=index.html
+
+@jd:body
+
+<p>Safety information for the <a href="index.html">ADT-1</a> device.</p>
+
+<p>
+  <strong>WARNING:</strong> Read all safety information below before using this device to avoid
+  injury.
+</p>
+<ul>
+  <li><p>Do not install near heat sources, such as heaters and other devices.</p></li>
+  <li><p>Use in a well-ventilated area and plug power adapter into an easily accessible
+      outlet. Only use this device with the provided power adapter.</p></li>
+  <li><p>The device has no on/off switch. To disconnect from power, you must unplug the
+      power adapter.</p></li>
+  <li><p>Only use indoors and do not expose to rain, liquid, moisture, excessive heat, or
+      naked flame.</p></li>
+  <li><p>Clean only with a dry cloth.</p></li>
+</ul>
+<p>
+  <strong>WARNING:</strong> Playing video games has been linked to injuries in some
+  users. Read all safety and health information below before using the gamepad to avoid possible
+  injury.
+</p>
+
+<p><u>Photosensitive Seizures</u></p>
+
+<p>
+  A very small percentage of people may experience a seizure when exposed to certain visual images,
+  including flashing lights or patterns that may appear in some video games, even people who have no
+  history of seizures or epilepsy. These seizures have a variety of symptoms, including
+  lightheadedness, altered vision, disorientation, loss of awareness, involuntary movements, loss of
+  consciousness, or convulsions. If you experience any of these symptoms, <u>stop gaming
+    immediately and consult your doctor</u>.
+</p>
+
+<p><u>Ergonomics</u></p>
+
+<p>Long periods of repetitive motion using incorrect body positioning may be associated with
+  physical discomfort and injuries to nerves, tendons, and muscles. If during or after gaming you
+  feel pain, numbness, weakness, swelling, burning, cramping, or stiffness, <u>stop gaming
+  and consult your doctor</u>.
+
+<p>
+  <strong>Healthy Gaming</strong>
+</p>
+
+<p>To reduce risk of seizures or injury, take the following precautions:</p>
+
+<ul>
+  <li><p>Sit as far away from the TV screen as possible.</p></li>
+  <li><p>Play in a well-lit room.</p></li>
+  <li><p>Do not play when you are drowsy or fatigued.</p></li>
+  <li><p>Take 10-15 minute breaks every hour if playing video games and avoid prolonged
+      gaming.</p></li>
+</ul>
+
+<p>
+  <strong>Do Not Attempt Repairs Yourself</strong>
+</p>
+
+<p>There are no user-serviceable parts inside. Do not attempt to open or disassemble.</p>
+
+<p>Failure to follow these safety instructions could result in fire, electric shock, damage to
+  the device or other property, or personal injury.</p>
+
+<hr />
+
+<p>
+  <strong>Importantes instructions concernant la sécurité</strong>
+</p>
+
+<p>
+  <strong>ATTENTION:</strong> Veuillez lire toutes les informations de sécurité énoncées ci-bas
+  avant d’utiliser l’appareil pour éviter des blessures.
+</p>
+
+<ul>
+  <li><p>Ne pas installer à proximité d’une source de chaleur telle une chaufferette ou un
+      autre appareil similaire.</p></li>
+  <li><p>Utiliser dans un endroit bien aéré et brancher l’adaptateur électrique dans une
+      prise de courant facilement accessible.</p></li>
+  <li><p>L’appareil ne possède aucun interrupteur marché/arrêt. Pour mettre l’appareil hors
+      tension, il faut débrancher l’appareil de la prise de courant.</p></li>
+  <li><p>Utiliser l’appareil uniquement à l’intérieur et ne pas l’exposer à la pluie, à des
+      substances liquides, à l’humidité, à la chaleur excessive ou à une flamme.</p></li>
+  <li><p>Nettoyer uniquement avec un linge sec.</p></li>
+</ul>
+
+<p>
+  <strong>ATTENTION:</strong> Le fait de jouer à des jeux vidéo a été relié à des blessures chez certains
+  utilisateurs. Afin d’éviter de possibles blessures, veuillez lire toutes les informations
+  concernant la sécurité et la santé énoncées ci-bas avant d’utiliser la tablette de jeu.
+</p>
+
+<p><u>Épilepsie photosensible</u></p>
+
+<p>L’exposition à certaines images visuelles, incluant les lumières ou motifs clignotants qui
+  peuvent apparaître dans certains jeux vidéo, peut provoquer chez un très faible pourcentage de
+  personnes une crise d’épilepsie, et ce, même si ces personnes n’ont aucun historique de crises ou
+  d’épilepsie. Ces crises comportent divers symptômes tels que des étourdissements, une vision
+  altérée, un sentiment de désorientation, la perte de conscience, des mouvements involontaires, la
+  perte de connaissance ou de conscience ou des convulsions. Si vous ressentez quelconque de ces
+  symptômes, <u>cessez de jouer immédiatement et consultez votre médecin</u>.</p>
+
+<p><u>Ergonomie</u></p>
+
+<p>Les longues périodes de mouvements répétitifs effectués dans une position corporelle
+  inadéquate peuvent mener à un inconfort physique et à des blessures aux nerfs, tendons et muscles.
+  Si durant ou après avoir joué à des jeux vidéo, vous ressentez de la douleur, de
+  l’engourdissement, une faiblesse, de l’inflammation, une sensation de brûlure, des crampes ou de
+  la rigidité, <u>cessez de jouer immédiatement et consultez votre médecin</u>.</p>
+
+<p>
+  <strong>Le jeu sécuritaire</strong>
+</p>
+
+<p>Afin de réduire les risques de crises d’épilepsie ou de blessures, veuillez prendre les
+  précautions suivantes :</p>
+
+<ul>
+  <li>Asseyez-vous aussi loin de l’écran de télévision que possible.</li>
+  <li>Jouez dans une pièce munie d’un éclairage adéquat.</li>
+  <li>Ne jouez pas lorsque vous êtes étourdi ou fatigué.</li>
+  <li>Prenez 10 à 15 minutes de pause après chaque heure de jeu et évitez les périodes de jeu
+  prolongées.</li>
+</ul>
+
+<p>
+  <strong>Ne pas tenter d’effectuer des réparations par vous-même</strong>
+</p>
+
+<p>L’Appareil ne contient aucune pièce pouvant être réparée par l’utilisateur. Ne pas tenter
+  d’ouvrir ou de désassembler l’Appareil.</p>
+
+<p>Le défaut de suivre ces instructions de sécurité pourrait provoquer un feu, un choc
+  électrique, un dommage à l’Appareil ou à d’autres objets ou des lésions corporelles.</p>
diff --git a/docs/html/preview/tv/games/index.jd b/docs/html/preview/tv/games/index.jd
new file mode 100644
index 0000000..b9de3a4
--- /dev/null
+++ b/docs/html/preview/tv/games/index.jd
@@ -0,0 +1,70 @@
+page.title=Games on TV
+page.tags="controller"
+
+@jd:body
+
+<p>This section complements the [larger best-practices guidance for designing for Android TV](TODO, use formal name of referenced doc, and add link). It assumes that you have read that guidance, and seeks to minimize repetition.</p>
+
+<h2>Overview</h2>
+<p>Because of factors including its large size, its control scheme, and its nature as a shared display, the television screen presents a number of considerations that may be new to mobile developers. This document breaks these considerations down into five sections:</p>
+<ul>
+<li>Display</li>
+<li>Control</li>
+<li>Manifest</li>
+<li>Google Play Game Services</li>
+<li>Web</li>
+</ul>
+<h2>Display</h2>
+<p>Large and centrally situated, the television screen imposes limitations, but also opens up new opportunities for immersive gameplay.</p>
+<h3>A shared display</h3>
+<p>A living-room TV poses design challenges for multiplayer games, in that all players can see everything. This issue is especially germane to games (such as card games or strategy games) that rely on each player’s possession of hidden information.</p>
+<p>Some mechanisms you can implement to address the problem of one player’s “eavesdropping” on another’s information are:</p>
+<ul>
+<li>A player might place a "blinder" on the screen to help conceal information. For example, in a turn-based game like a word or card game, one player at a time might view the display. When the player finishes a move, the game allows him or her to cover the screen with a “blinder” that blocks anyone from viewing secret information. When the next player begins a turn, the blinder opens to reveal his or her own information.</li>
+<li>A second screen, such as a handset or larger device, can enable a player to conceal information. For information on implementing second-screen support, see <a href="http://developer.android.com/reference/android/app/Presentation.html">Presentation</a> on the Android developer site.</li>
+</ul>
+<h3>No touch interface</h3>
+<p>A television does not have a touch interface. Your game design, therefore, need not take into account the possibility that a player’s controlling fingers might block the on-screen action. You can assume constant visibility of the entire viewing area.</p>
+<p>See the <a href=#control>Control</a> section in this document and in [Design for TV](TODO, use formal name of referenced doc, and add link) for more implications of the lack of touch interface.</p>
+<h3>Landscape display</h3>
+<p>In mobile-device terms, a TV is always “sideways.” You can’t turn it, and there is no portrait orientation. You should always be designing your TV games to be displayed in landscape mode.</p>
+<a id=control><h2>Control</h2>
+<p>Without a touch interface, it's even more important than usual to get your controls right, so that players find them intuitive and fun to use. The separation of controller from device also introduces some other issues to pay attention to, like keeping track of multiple players' controllers, and handling disconnects gracefully.</p>
+<h3>D-pad</h3>
+<p>Because of the lack of touch interface, you should be planning your control scheme based on a D-pad. Some key points to keep in mind include:</p>
+<p>The player needs to use the gamepad in all aspects of the game&ndash;not just controlling core gameplay, but also navigating menus and ads. For this reason, you should also ensure that your Android TV game does not refer to a touch interface: for example, an Android TV game cannot tell a player to "Tap to skip".</p>
+<p>You can avoid unhappy surprises (and resulting low ratings) by using your Play Store description to communicate to the player any expectations about controllers. If a game is better suited to a gamepad with a joystick than one with only a D-pad, you should make this clear. A player who uses an ill-suited controller for a game is likely to have a subpar experience&ndash;and penalize your game in the ratings.</p>
+<p>You can also help ensure a good player experience by ensuring that button mapping is intuitive and flexible. For example, you can adhere to accepted custom by using the A button to <code>Accept</code>, and the B button to <code>Cancel</code>. You can also offer flexibility in the form of remappability. For more information on button mapping, see <a href="http://developer.android.com/training/game-controllers/controller-input.html">Handling Controller Actions</a>.</p>
+<p>Your game can also contribute to a good match between controller and game by querying the controller about its capabilities. For example, you may intend for a player to steer an object by waving the controller in the air. If a player's controller lacks accelerometer and gyroscope hardware, however, waving will not work. But when your game queries the controller and discovers that motion detection is not supported, it can switch over to an alternative, available control scheme.</p>
+<p>For more information on querying controller capabilities, see <a href="http://developer.android.com/training/game-controllers/compatibility.html">Supporting Controllers Across Android Versions</a>.</p>
+<h3>Back-button behavior</h3>
+<p>The Back button should never act as a toggle. For example, do not use it to both open and close a menu. Its behavior should only be linear. For example: Game play &gt; Game pause screen &gt; Game main screen &gt; Android home screen.</p>
+<p>With this principle of "linear navigation" in mind, you <b>may</b> use the back button to leave an in-game menu (opened by a different button) and return to gameplay.</p>
+<h3>Handling multiple controllers</h3>
+<p>When multiple players are playing a game, each with his or her own controller, it is important to map each player-controller pair. For information on how to implement controller-number identification, see <a href="http://developer.android.com/reference/android/view/InputDevice.html#getControllerNumber(">Input Devices</a>) on the Android developer site.</p>
+<h3>Handling disconnects</h3>
+<p>When a controller is disconnected in the middle of gameplay, the game should pause, and a dialog should appear prompting the disconnected player to reconnect his or her controller.</p>
+<p>The dialog should also offer troubleshooting tips (e.g., "Check your Bluetooth connection").</p>
+<h2>Manifest</h2>
+<p>Games are displayed in a separate row from regular apps in the launcher. Android TV uses the <code>android:isGame</code> flag to differentiate games from non-game apps. You can assign it a value of either <code>true</code> or <code>false</code>. For example:</p>
+<pre class="fragment">&lt;application&gt;
+ . . .
+ &lt;meta-data android:name="isGame" android:value=["true" | "false"]/&gt;
+android:isGame=["true" | "false"] &gt;
+ . . .
+&lt;/application&gt;
+</pre><h2>Google Play Game Services</h2>
+<p>If your game integrates Google Play Game Services, you should keep in mind a number of considerations pertaining to achievements, sign-on, saving games, and multiplayer play.</p>
+<h3>Achievements</h3>
+<p>Your game should include at least five (earnable) achievements. Only a user controlling gameplay from a supported input device should be able to earn achievements.</p>
+<h3>Sign-on</h3>
+<p>Your game should attempt to sign the user in on launch. If the player declines sign-in several times in a row, your game should stop asking.</p>
+<h3>Saving</h3>
+<p>We highly recommend using Play Services cloud save to store your game save. Your game should bind game saves to a specific Google account, so as to be uniquely identifiable even across devices: Whether the player is using a handset or a TV, the game should be able to pull the same game-save information from his or her account.</p>
+<p>You should also provide an option in your game's UI to prompt the player to destroy save data. You might put the option in the game's <code>Settings</code> screen.</p>
+<h3>Multiplayer experience</h3>
+<p>A game offering a multiplayer experience must allow at least two players to enter a room.</p>
+<h2>Web</h2>
+<p>Android TV games do not support a full web browser. You should therefore avoid using generic URLs in your game.</p>
+<p>Webviews will work for logins to services like Google+ and Facebook. </p>
+
diff --git a/docs/html/preview/tv/images/android-tv-remote.png b/docs/html/preview/tv/images/android-tv-remote.png
new file mode 100644
index 0000000..d15fbc5
--- /dev/null
+++ b/docs/html/preview/tv/images/android-tv-remote.png
Binary files differ
diff --git a/docs/html/preview/tv/images/home-recommendations.png b/docs/html/preview/tv/images/home-recommendations.png
new file mode 100644
index 0000000..ef97145
--- /dev/null
+++ b/docs/html/preview/tv/images/home-recommendations.png
Binary files differ
diff --git a/docs/html/preview/tv/index.jd b/docs/html/preview/tv/index.jd
new file mode 100644
index 0000000..dd35908
--- /dev/null
+++ b/docs/html/preview/tv/index.jd
@@ -0,0 +1,22 @@
+page.title=Android TV Apps
+
+@jd:body
+
+<p>Android offers a rich user experience that's optimized for apps running on large screen
+  devices, such as high-definition televisions. Apps on TV offer new opportunities to
+  delight your users from the comfort of their couch.</p>
+
+<p>This guide helps you build apps for TV devices, including:</p>
+
+<ul>
+  <li>How to set up your development environment</li>
+  <li>How to build user interfaces for TV</li>
+  <li>Guidelines for building games for TV</li>
+</ul>
+
+<p>Prepare your app for its big screen debut!</p>
+
+<p>
+  <strong><a href="{@docRoot}preview/tv/start/index.html">Get Started &gt;</a></strong>
+</p>
+
diff --git a/docs/html/preview/tv/start/hardware-features.jd b/docs/html/preview/tv/start/hardware-features.jd
new file mode 100644
index 0000000..ddec496
--- /dev/null
+++ b/docs/html/preview/tv/start/hardware-features.jd
@@ -0,0 +1,183 @@
+page.title=Hardware Features on TV
+page.tags="unsupported"
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#unsupported-features">Unsupported Hardware Features</a></li>
+    <li><a href="#workaround-features">Handling Unsupported Features</a></li>
+    <li><a href="#check-features">Checking Available Features</a>
+      <ol>
+        <li><a href="#no-touchscreen">Touch screen</a></li>
+        <li><a href="#no-camera">Camera</a></li>
+        <li><a href="#no-gps">GPS</a></li>
+      </ol>
+
+    </li>
+  </ol>
+</div>
+</div>
+
+<p>TVs do not have some of the hardware features found on other Android devices.
+Touch screens, cameras, and GPS receivers are some of the most commonly used hardware features
+which are typically not available on a TV. When you build an app for TV, you must carefully
+consider if your app can handle not having these features and, if necessary, work around them.</p>
+
+<p>This guide discusses the hardware features not available on TV devices and shows you how to
+work around those limitations in your app. For more information on filtering and declaring
+features in the manifest, see the
+<a href="{@docRoot}guide/topics/manifest/uses-feature-element.html">uses-feature</a> guide.</p>
+
+
+<h2 id="unsupported-features">Unsupported Hardware Features</h2>
+
+<p>TVs have a different purpose from other devices, and so they do not have hardware
+features that other Android-powered devices often have. For this reason, the Android system
+does not support the following features for a TV device:
+
+<table>
+<tr>
+<th>Hardware</th>
+<th>Android feature descriptor</th>
+</tr>
+<tr>
+<td>Camera</td>
+<td>android.hardware.camera</td>
+</tr>
+<tr>
+<td>GPS</td>
+<td>android.hardware.location.gps</td>
+</tr>
+<tr>
+<td>Microphone</td>
+<td>android.hardware.microphone</td>
+</tr>
+<tr>
+<td>Near Field Communications (NFC)</td>
+<td>android.hardware.nfc</td>
+</tr>
+<tr>
+<td>Telephony</td>
+<td>android.hardware.telephony</td>
+</tr>
+<tr>
+<td>Touchscreen</td>
+<td>android.hardware.touchscreen</td>
+</tr>
+</table>
+</p>
+
+
+<h2 id="check-features">Checking Available Features</h2>
+
+<p>To check if a feature is available at runtime, call {@link
+  android.content.pm.PackageManager#hasSystemFeature(String)}. This method takes a single string
+  argument that specifies the feature you want to check. For example, to check for a touch screen,
+  use {@link android.content.pm.PackageManager#hasSystemFeature(String)} with the argument
+  {@link android.content.pm.PackageManager#FEATURE_TOUCHSCREEN}.</p>
+
+<p>The following code example demonstrates how to detect the availability of a hardware features
+  at runtime:</p>
+
+<pre>
+// Check if the telephony hardware feature is available.
+if (getPackageManager().hasSystemFeature("android.hardware.telephony")) {
+    Log.d("Mobile Test", "Running on phone");
+// Check if android.hardware.touchscreen feature is available.
+} else if (getPackageManager().hasSystemFeature("android.hardware.touchscreen")) {
+    Log.d("Tablet Test", "Running on devices that don't support telephony but "+
+            "do have a touch screen.");
+} else {
+    Log.d("TV Test", "Running on a TV!");
+}
+</pre>
+
+<p class="note">
+  <strong>Note:</strong> You can also use the {@link android.app.UiModeManager#getCurrentModeType
+  UiModeManager.getCurrentModeType()} method to detect the current platform type. For TV devices,
+  this method returns a value of {@link android.content.res.Configuration#UI_MODE_TYPE_TELEVISION
+  Configuration.UI_MODE_TYPE_TELEVISION}.
+</p>
+
+
+<h2 id="workaround-features">Handling Unsupported Features</h2>
+
+<p>Depending on the design and functionality of your app, you may be able to work around certain
+  hardware features being unavailable. This section discusses how to work around specific hardware
+  features.</p>
+
+
+<h3 id="no-touchscreen">Touch screen</h3>
+
+<p>Android doesn't support touch screen interaction for TV devices, since most TVs don't have touch
+  screens, and using a touch screen is not consistent with a viewing environment where the user is
+  seated 10 feet away from the display.</p>
+
+<p>On TV devices, you should work around this limitation by supporting navigation using a directional
+  pad (D-pad) on TV remote control. For more information on properly supporting navigation using
+  TV-friendly controls, see <a href="{@docRoot}preview/tv/ui/navigation.html">Navigation for
+  TV</a>.</p>
+
+<p>You can explicitly declare if your application requires (or does not require) a touch screen
+  by including the following entry in your manifest:</p>
+
+<pre>
+&lt;uses-feature android:name="android.hardware.touchscreen"
+        android:required="false"/&gt;
+</pre>
+
+
+<h3 id="no-camera">Camera</h3>
+
+<p>Although a TV typically does not have a camera, you can still provide a photography-related
+  application on a TV. For example, if you have an app that takes, views and edits photos, you can
+  disable its picture-taking functionality for TVs and still allow users to view and even edit
+  photos. If you decide that you want to enable your camera-related application to work on a
+  TV device without a camera, you can add an attribute to your app manifest declaring that
+  a camera is not required by your app:</p>
+
+<pre>
+&lt;uses-feature android:name="android.hardware.camera" android:required="false" /&gt;
+</pre>
+
+<p>If you enable your application to run without a camera, you should add code to your application
+that detects if the camera feature is available and makes adjustments to the operation of your app.
+The following code example demonstrates how to detect the presence of a camera:</p>
+
+<pre>
+// Check if the camera hardware feature is available.
+if (getPackageManager().hasSystemFeature("android.hardware.camera")) {
+    Log.d("Camera test", "Camera available!");
+} else {
+    Log.d("Camera test", "No camera available. View and edit features only.");
+}
+</pre>
+
+
+<h3 id="no-gps">GPS</h3>
+
+<p>TVs are stationary, indoor devices, and do not have built-in global positioning system (GPS)
+  receivers. If your application uses location information, you can still allow users to search
+  for a location, or use a static location provider such as a zip code configured during the
+  TV device setup.</p>
+
+<pre>
+LocationManager locationManager = (LocationManager) this.getSystemService(
+        Context.LOCATION_SERVICE);
+Location location = locationManager.getLastKnownLocation("static");
+Geocoder geocoder = new Geocoder(this);
+Address address = null;
+
+try {
+  address = geocoder.getFromLocation(location.getLatitude(),
+          location.getLongitude(), 1).get(0);
+  Log.d("Zip code", address.getPostalCode());
+
+} catch (IOException e) {
+  Log.e(TAG, "Geocoder error", e);
+}
+</pre>
+
diff --git a/docs/html/preview/tv/start/index.jd b/docs/html/preview/tv/start/index.jd
new file mode 100644
index 0000000..11d6ad3
--- /dev/null
+++ b/docs/html/preview/tv/start/index.jd
@@ -0,0 +1,233 @@
+page.title=Get Started with TV Apps
+page.tags="leanback","recyclerview","launcher"
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#prerequisites">Prerequisites</a></li>
+    <li><a href="#dev-project">Setup a TV Project</a>
+      <ul>
+        <li><a href="#tv-activity">Create a TV Activity</a></li>
+        <li><a href="#tv-libraries">Add TV Support Libraries</a></li>
+      </ul>
+    </li>
+    <li><a href="#build-it">Build TV Apps</a></li>
+    <li><a href="#run">Run TV Apps</a></li>
+
+  </ol>
+</div>
+</div>
+
+<p>This guide describes how to prepare your development environment and projects for building
+  TV apps, including updating your existing app to run on TV devices.</p>
+
+
+<h2 id="prerequisites">Prerequisites</h2>
+
+<p>Before you begin setting up to build apps for TV, you must:</p>
+
+<ul>
+  <li><strong><a href="{@docRoot}preview/setup-sdk.html">
+    Set up the Preview SDK</a></strong>
+    <br>
+    The preview SDK provides the developer tools needed to build and test apps for TV.
+  </li>
+  <li><strong><a href="{@docRoot}preview/setup-sdk.html#project">
+    Create a Preview SDK Project</a></strong>
+    <br>
+    In order to access new APIs for TV devices, you must create a project that targets the preview
+    release level or modify an existing project to target the preview release.
+  </li>
+</ul>
+
+
+<h2 id="dev-project">Set up a TV Project</h2>
+
+<p>TV apps use the same structure as those for phones and tablets. This means you can modify
+  your existing apps to also run on TV devices or create new apps based on what you already know
+  about building apps for Android. This section discusses how to modify an existing app, or create a
+  new one, to run on TV devices.</p>
+
+<p>These are the main steps to creating an app that runs on TV devices. Only the first
+  is required:</p>
+
+<ul>
+  <li><strong>Activity for TV</strong> - (Required) In your application manifest, you must
+    declare an activity that is intended to run on TV devices.</li>
+  <li><strong>TV Support Libraries</strong> - (Optional) There are several Support Libraries
+    available for TV devices that provide widgets for building user interfaces.</li>
+</ul>
+
+
+<h3 id="tv-activity">Create a TV Activity</h3>
+
+<p>An application intended to run on TV devices must declare a launcher activity for TV
+  in its manifest using a {@code android.intent.category.LEANBACK_LAUNCHER} intent filter.
+  This filter identifies your app as being built for TV, enabling it to be displayed in the
+  Google Play store app running on TV devices. Declaring this intent also identifies which activity
+  in your app should be launched when a user selects its icon on the TV home screen.</p>
+
+<p class="caution">
+  <strong>Caution:</strong> If you do not include the {@code LEANBACK_LAUNCHER} intent filter in
+  your app, it is not visible to users running the Google Play store on TV devices. Also, if your
+  app does not have this filter when you load it onto a TV device using developer tools, the app
+  does not appear in the TV user interface.
+</p>
+
+<p>The following code snippet shows how to include this intent filter in your manifest:</p>
+
+<pre>
+&lt;application&gt;
+  ...
+  &lt;activity
+    android:name=&quot;com.example.android.MainActivity&quot;
+    android:label=&quot;@string/app_name&quot; &gt;
+
+    &lt;intent-filter&gt;
+      &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
+      &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
+    &lt;/intent-filter&gt;
+  &lt;/activity&gt;
+
+  &lt;activity
+    android:name=&quot;com.example.android.<strong>TvActivity</strong>&quot;
+    android:label=&quot;&#64;string/app_name&quot;
+    android:theme=&quot;&#64;android:style/Theme.Leanback&quot;&gt;
+
+    &lt;intent-filter&gt;
+      &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;
+      &lt;category android:name="<strong>android.intent.category.LEANBACK_LAUNCHER</strong>" /&gt;
+    &lt;/intent-filter&gt;
+
+  &lt;/activity&gt;
+&lt;/application&gt;
+</pre>
+
+<p>The second activity manifest entry in the example above specifies that activity as
+  the main one when your app launches on an TV device.</p>
+
+<p>If you have an existing app that you are modifying for TV use, your app should not use the same
+  activity layout for TV that it does for phones and tablets. The user interface of your TV app (or
+  TV portion of your existing app) should provide a simpler interface that can be easily navigated
+  using a remote control from a couch. For guidelines on designing an app for TV, see the
+  <a href="{@docRoot}design/tv/index.html">TV Design</a> guide.  For more instructions on
+  developing a user interface appropriate to TV, see the
+  <a href="{@docRoot}preview/tv/ui/index.html">TV User Interface</a> guide.
+</p>
+
+
+<h3 id="tv-libraries">Add TV Support Libraries</h3>
+
+<p>The Preview SDK includes support libraries that are intended for use with TV apps. These
+  libraries provide APIs and user interface widgets for use on TV devices. The libraries are
+  located in the {@code &lt;sdk&gt;/extras/android/support/} directory where you installed the
+  Preview SDK. Here is a list of the libraries and their general purpose:</p>
+
+<ul>
+  <li><strong>v17 leanback library</strong> - Provides user interface widgets for TV, including
+    {@code BrowseFragment}, {@code DetailsFragment}, and {@code SearchFragment}.
+    <ul>
+      <li>SDK location: {@code &lt;sdk&gt;/extras/android/support/v17/leanback}</li>
+      <li>Gradle dependency: {@code com.android.support:leanback-v17:20.0.+}</li>
+      <li>Contains resources: Yes</li>
+    </ul>
+  </li>
+  <li><strong>v7 recyclerview library</strong> - Provides classes for managing display of long
+  lists in a memory efficient manner. Several classes in the v17 leanback library depend on the
+  classes in this library.
+    <ul>
+      <li>SDK location: {@code &lt;sdk&gt;/extras/android/support/v7/recyclerview}</li>
+      <li>Gradle dependency: {@code com.android.support:recyclerview-v7:20.0.+}</li>
+      <li>Contains resources: No</li>
+    </ul>
+  </li>
+</ul>
+
+<p class="note">
+  <strong>Note:</strong> You are not required to use these support libraries for your TV app.
+  However, we strongly recommend using them, particularly for apps that provide a media catalog
+  browsing interface.
+</p>
+
+<p>If you decide to use the v17 leanback library for your app, you should note that it is
+  dependent on the <a href="{@docRoot}tools/support-library/features.html#v7-appcompat">v7
+  appcompat library</a>, which is, in turn, dependent on the
+  <a href="{@docRoot}tools/support-library/features.html#v4">v4 support library</a>. This means
+  that apps that use the leanback support library should include all of these support
+  libraries:</p>
+
+<ul>
+  <li>v17 leanback support library</li>
+  <li>v7 recyclerview support library</li>
+  <li>v7 appcompat support library</li>
+  <li>v4 support library</li>
+</ul>
+
+<p>Two of these libraries (v17 leanback and v7 appcompat) contain resources, which require
+  you to take specific steps to include them in app projects. For instructions on
+  importing a support library with resources, see
+  <a href="http://developer.android.com/tools/support-library/setup.html#libs-with-res">
+  Support Library Setup</a>.
+</p>
+
+
+<h2 id="build-it">Build TV Apps</h2>
+
+<p>After you have completed the steps described above, it's time to start building apps for
+  the big screen! Check out these additional topics to help you build your app for TV:
+
+<ul>
+  <li><a href="{@docRoot}preview/tv/ui/index.html">User Interface</a> - The user interface of
+    TV devices is different from those of other Android devices. See this topic to find out how
+    to build TV user interfaces and to learn about the widgets provided to simplify that task.
+  </li>
+  <li><a href="{@docRoot}preview/tv/games/index.html">Games for TV</a> - TV devices are great
+    platforms for games. See this topic for information on building great game experiences for
+    TV.</li>
+  <li><a href="{@docRoot}preview/tv/start/hardware-features.html">Hardware features</a> - TV
+    devices do not contain hardware features normally found on other Android devices. See this
+    topic for information on unsupported hardware features and what to do about them.
+  </li>
+</ul>
+
+
+<h2 id="run">Run TV Apps</h2>
+
+<p>Running your app is an important part of the development process. The AVD Manager in the
+  Android SDK provides the device definitions that allows you to create virtual TV devices for
+  running and testing your applications.</p>
+
+<p>To create an virtual TV device:</p>
+
+<ol>
+  <li>Start the AVD Manager. For more information, see the
+    <a href="{@docRoot}tools/help/avd-manager.html">AVD Manager</a> help.</li>
+  <li>In the AVD Manager dialog, click the <strong>Device Definitions</strong> tab.</li>
+  <li>Select one of the Android TV device definitions, such as
+    <strong>Large Android TV</strong>, and click <strong>Create AVD</strong>.</li>
+  <li>Select the emulator options and click <strong>OK</strong> to create the AVD.
+    <p class="note">
+      <strong>Note:</strong> For best performance of the TV emulator device, enable the <strong>Use
+      Host GPU</strong> option and CPU platform image that supports hardware acceleration. For
+      more information on hardware acceleration of the emulator, see
+      <a href="{@docRoot}tools/devices/emulator.html#acceleration">Using the Emulator</a>.
+    </p>
+  </li>
+</ol>
+
+<p>To test your application on the virtual TV device:</p>
+
+<ol>
+  <li>Compile your TV application in your development environment.</li>
+  <li>Run the application from your development environment and choose the TV virtual device as
+  the target.</li>
+</ol>
+
+<p>For more information about using emulators see, <a href="{@docRoot}tools/devices/emulator.html">
+Using the Emulator</a>. For more information about deploying apps to emulators from
+Eclipse with ADT, see <a href="{@docRoot}http://developer.android.com/tools/building/building-eclipse.html">
+Building and Running from Eclipse with ADT</a>.</p>
+
diff --git a/docs/html/preview/tv/ui/browse.jd b/docs/html/preview/tv/ui/browse.jd
new file mode 100644
index 0000000..d7a1fb6
--- /dev/null
+++ b/docs/html/preview/tv/ui/browse.jd
@@ -0,0 +1,199 @@
+page.title=BrowseFragment
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#layout">Media Browse Layout</a></li>
+    <li><a href="#lists">Displaying Media Lists</a></li>
+    <li><a href="#background">Updating the Background</a></li>
+  </ol>
+
+</div>
+</div>
+
+<p>The <a href="{@docRoot}preview/tv/start/index.html#tv-libraries">Leanback support library</a>
+  provides several APIs for displaying and browsing media catalogs
+  on the TV devices. This guide discusses how to use the classes provided by this library to
+  implement a user interface for browsing music or videos from your app's media catalog.</p>
+
+
+<h2 id="layout">Media Browse Layout</h2>
+
+<p>The {@code BrowseFragment} class in the Leanback support library allows you to create a primary
+  layout for browsing categories and rows of media items with a minimum of code. The following
+  example shows how to create a layout that contains a {@code BrowseFragment}:</p>
+
+<pre>
+&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  android:layout_width=&quot;match_parent&quot;
+  android:layout_height=&quot;match_parent&quot;
+  android:orientation=&quot;vertical&quot;
+  &gt;
+
+  &lt;fragment
+      <strong>android:name="android.support.v17.leanback.app.BrowseFragment"</strong>
+      android:id=&quot;@+id/browse_fragment&quot;
+      android:layout_width=&quot;match_parent&quot;
+      android:layout_height=&quot;match_parent&quot;
+      /&gt;
+&lt;/LinearLayout&gt;
+</pre>
+
+<p>In order to work with this layout in an activity, retrieve the {@code BrowseFragment} element
+  from the layout. Use the methods in {@code BrowseFragment} to set display parameters such as the
+  icon, title and whether category headers are enabled. The following code sample demonstrates how
+  to set the layout parameters for a {@code BrowseFragment} in a layout:</p>
+
+<pre>
+public class BrowseMediaActivity extends Activity {
+
+    public static final String TAG ="BrowseActivity";
+
+    protected BrowseFragment mBrowseFragment;
+
+    &#64;Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.browse_fragment);
+
+        final FragmentManager fragmentManager = getFragmentManager();
+        <strong>mBrowseFragment = (BrowseFragment) fragmentManager.findFragmentById(
+                R.id.browse_fragment);</strong>
+
+        // Set display parameters for the BrowseFragment
+        mBrowseFragment.setHeadersState(BrowseFragment.HEADERS_ENABLED);
+        mBrowseFragment.setTitle(getString(R.string.app_name));
+        mBrowseFragment.setBadgeDrawable(getResources().getDrawable(R.drawable.ic_launcher));
+        mBrowseFragment.setBrowseParams(params);
+
+    }
+}
+</pre>
+
+
+<h2 id="lists">Displaying Media Lists</h2>
+
+<p>The {@code BrowseFragment} allows you to define and display browsable media content categories and
+  media items from a media catalog using adapters and presenters. Adapters enable you to connect to
+  local or online data sources that contain your media catalog information. Presenter classes hold
+  data about media items and provide layout information for displaying an item on screen.</p>
+
+<p>The following example code shows an implementation of a presenter for displaying string
+  data:</p>
+
+<pre>
+public class StringPresenter extends Presenter {
+    private static final String TAG = "StringPresenter";
+
+    public ViewHolder onCreateViewHolder(ViewGroup parent) {
+        TextView textView = new TextView(parent.getContext());
+        textView.setFocusable(true);
+        textView.setFocusableInTouchMode(true);
+        textView.setBackground(
+                parent.getContext().getResources().getDrawable(R.drawable.text_bg));
+        return new ViewHolder(textView);
+    }
+
+    public void onBindViewHolder(ViewHolder viewHolder, Object item) {
+        ((TextView) viewHolder.view).setText(item.toString());
+    }
+
+    public void onUnbindViewHolder(ViewHolder viewHolder) {
+        // no op
+    }
+}
+</pre>
+
+<p>Once you have constructed a presenter class for your media items, you can build and attach an
+  adapter to the {@code BrowseFragment} to display those items on screen for browsing by the user. The
+  following example code demonstrates how to construct an adapter to display categories and items
+  in those categories using the StringPresenter class shown in the previous code example:</p>
+
+<pre>
+private ArrayObjectAdapter mRowsAdapter;
+private static final int NUM_ROWS = 4;
+
+&#64;Override
+protected void onCreate(Bundle savedInstanceState) {
+    ...
+
+    buildRowsAdapter();
+}
+
+private void buildRowsAdapter() {
+    mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+
+    for (int i = 0; i &lt; NUM_ROWS; ++i) {
+        ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
+                new StringPresenter());
+        listRowAdapter.add("Media Item 1");
+        listRowAdapter.add("Media Item 2");
+        listRowAdapter.add("Media Item 3");
+        HeaderItem header = new HeaderItem(i, "Category " + i, null);
+        mRowsAdapter.add(new ListRow(header, listRowAdapter));
+    }
+
+    mBrowseFragment.setAdapter(mRowsAdapter);
+}
+</pre>
+
+<p>This example shows a static implementation of the adapters. A typical media browsing
+  application uses data from an online database or web service. For an example of a browsing
+  application that uses data retrieved from the web, see the
+  <a href="http://github.com/googlesamples/androidtv-leanback">Android TV</a> sample app.</p>
+
+
+<h2 id="background">Updating the Background</h2>
+
+<p>In order to add visual interest to a media-browsing app on TV, you can update the background
+  image as users browse through content. This technique can make interaction with your app feel more
+  cinematic and enjoyable for users.</p>
+
+<p>The Leanback support library provides a {@link
+  android.support.v17.leanback.app.BackgroundManager} class for changing the background of your TV
+  app activity. The following example shows how to create a simple method for updating the
+  background within your TV app activity:</p>
+
+<pre>
+protected void updateBackground(Drawable drawable) {
+    BackgroundManager.getInstance(this).setDrawable(drawable);
+}
+</pre>
+
+<p>Many of the existing media-browse apps automatically update the background as the user
+  navigates through media listings. In order to do this, you can set up a selection listener to
+  automatically update the background based on the user's current selection. The following example
+  shows you how to set up an {@link android.support.v17.leanback.widget.OnItemSelectedListener}
+  class to catch selection events and update the background:</p>
+
+<pre>
+protected void clearBackground() {
+    BackgroundManager.getInstance(this).setDrawable(mDefaultBackground);
+}
+
+protected OnItemSelectedListener getDefaultItemSelectedListener() {
+    return new OnItemSelectedListener() {
+        &#64;Override
+        public void onItemSelected(Object item, Row row) {
+            if (item instanceof Movie ) {
+                URI uri = ((Movie)item).getBackdropURI();
+                updateBackground(uri);
+            } else {
+                clearBackground();
+            }
+        }
+    };
+}
+</pre>
+
+<p class="note">
+  <strong>Note:</strong> The implementation above is a simple example shown for purposes of
+  illustration. When creating this function in your own app, you should consider running the
+  background update action in a separate thread for better performance. In addition, if you are
+  planning on updating the background in response to users scrolling through items, consider adding
+  a time to delay a background image update until the user settles on an item. This technique avoids
+  excessive background image updates.
+</p>
diff --git a/docs/html/preview/tv/ui/details.jd b/docs/html/preview/tv/ui/details.jd
new file mode 100644
index 0000000..8b8fa8b5
--- /dev/null
+++ b/docs/html/preview/tv/ui/details.jd
@@ -0,0 +1,214 @@
+page.title=DetailFragment
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#details-presenter">Build a Details Presenter</a></li>
+    <li><a href="#details-fragment">Extend the Details Fragment</a>
+      <li><a href="#activity">Creating a Details Activity</a></li>
+      <li><a href="#item-listener">Listener for Clicked Items</a></li>
+    </li>
+  </ol>
+</div>
+</div>
+
+<p>The media browsing interface classes provided by the
+  <a href="{@docRoot}preview/tv/start/index.html#tv-libraries">Leanback support library</a>
+  include classes for displaying additional information about a media item, such as a description
+  or reviews, and for taking action on that item, such as purchasing it or playing its content. This
+  section discusses how to create a presenter class for media item details and extend the
+  {@code DetailsFragment} class to implement a details view for a media item when it
+  is selected by a user.
+</p>
+
+<p class="note">
+  <strong>Note:</strong> The implementation example shown here uses an additional activity to
+  contain the {@code DetailsFragment}. However, it is possible to avoid creating a second activity
+  by replacing the current {@code BrowseFragment} with a {@code DetailsFragment} within the <em>same</em>
+  activity using fragment transactions. For more information on using fragment transactions, see the
+  <a href="{@docRoot}training/basics/fragments/fragment-ui.html#Replace">Building a Dynamic
+    UI with Fragments</a> training.
+</p>
+
+
+<h2 id="details-presenter">Build a Details Presenter</h2>
+
+<p>In the media browsing framework provided for by the leanback support library, you use
+  presenter objects to control the display of data on screen, including media item details. The
+  framework provides the {@code AbstractDetailsDescriptionPresenter} class for this purpose, which
+  is a nearly complete implementation of the presenter for media item details. All you have to do is
+  implement the {@code onBindDescription()} method to bind the view fields to your data objects, as shown in
+  the following code sample:</p>
+
+<pre>
+public class DetailsDescriptionPresenter
+        extends AbstractDetailsDescriptionPresenter {
+
+    &#64;Override
+    protected void onBindDescription(ViewHolder viewHolder, Object itemData) {
+        MyMediaItemDetails details = (MyMediaItemDetails) itemData;
+        // In a production app, the itemData object contains the information
+        // needed to display details for the media item:
+        // viewHolder.getTitle().setText(details.getShortTitle());
+
+        // Here we provide static data for testing purposes:
+        viewHolder.getTitle().setText(itemData.toString());
+        viewHolder.getSubtitle().setText("2014   Drama   TV-14");
+        viewHolder.getBody().setText("Lorem ipsum dolor sit amet, consectetur "
+                + "adipisicing elit, sed do eiusmod tempor incididunt ut labore "
+                + " et dolore magna aliqua. Ut enim ad minim veniam, quis "
+                + "nostrud exercitation ullamco laboris nisi ut aliquip ex ea "
+                + "commodo consequat.");
+    }
+}
+</pre>
+
+
+<h2 id="details-fragment">Extend the Details Fragment</h2>
+
+<p>When you use the {@code DetailsFragment} class for displaying your media item details, you
+  extend that class to provide additional content such as a preview image and actions for the media
+  item. You can also provide additional content, such as a list of related media items.</p>
+
+<p>The following example code demonstrates how to use the presenter class you created in the
+  previous section, add a preview image and actions for the media item being viewed. This example
+  also shows the addition of a related media items row, which appears below the details listing.</p>
+
+<pre>
+public class MediaItemDetailsFragment extends DetailsFragment {
+    private static final String TAG = "MediaItemDetailsFragment";
+    private ArrayObjectAdapter mRowsAdapter;
+
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        Log.i(TAG, "onCreate");
+        super.onCreate(savedInstanceState);
+
+        buildDetails();
+    }
+
+    private void buildDetails() {
+        ClassPresenterSelector selector = new ClassPresenterSelector();
+        // Attach your media item details presenter to the row presenter:
+        DetailsOverviewRowPresenter rowPresenter =
+            new DetailsOverviewRowPresenter(new DetailsDescriptionPresenter());
+
+        selector.addClassPresenter(DetailsOverviewRow.class, rowPresenter);
+        selector.addClassPresenter(ListRow.class,
+                new ListRowPresenter());
+        mRowsAdapter = new ArrayObjectAdapter(selector);
+
+        Resources res = getActivity().getResources();
+        DetailsOverviewRow detailsOverview = new DetailsOverviewRow(
+                "Media Item Details");
+
+        // Add images and action buttons to the details view
+        detailsOverview.setImageDrawable(res.getDrawable(R.drawable.jelly_beans));
+        detailsOverview.addAction(new Action(1, "Buy $9.99"));
+        detailsOverview.addAction(new Action(2, "Rent $2.99"));
+        mRowsAdapter.add(detailsOverview);
+
+        // Add a Related items row
+        ArrayObjectAdapter listRowAdapter = new ArrayObjectAdapter(
+                new StringPresenter());
+        listRowAdapter.add("Media Item 1");
+        listRowAdapter.add("Media Item 2");
+        listRowAdapter.add("Media Item 3");
+        HeaderItem header = new HeaderItem(0, "Related Items", null);
+        mRowsAdapter.add(new ListRow(header, listRowAdapter));
+
+        setAdapter(mRowsAdapter);
+    }
+}
+</pre>
+
+
+<h3 id="activity">Creating a Details Activity</h3>
+
+<p>Fragments such as the {@code DetailsFragment} must be contained within an activity in order
+  to be used for display. Creating an activity for your details view, separate from the browse
+  activity, enables you to invoke your details view using an Intent. This section explains how to
+  build an activity to contain your implementation of the detail view for your media items.</p>
+
+<p>Start creating the details activity by building a layout that references your implementation
+  of the {@code DetailsFragment}:</p>
+
+<pre>
+&lt;!-- file: res/layout/details.xml --&gt;
+
+&lt;fragment xmlns:android="http://schemas.android.com/apk/res/android"
+    <strong>android:name="com.example.android.mediabrowser.MediaItemDetailsFragment"</strong>
+    android:id="&#64;+id/details_fragment"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+/&gt;
+</pre>
+
+<p>Next, create an activity class that uses the layout shown in the previous code example:</p>
+
+<pre>
+public class DetailsActivity extends Activity
+{
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        <strong>setContentView(R.layout.details);</strong>
+    }
+}
+</pre>
+
+<p>Finally, add this new activity to the manifest. Remember to apply the Leanback theme to
+  ensure that the user interface is consistent with the media browse activity:</p>
+
+<pre>
+&lt;application&gt;
+  ...
+
+  &lt;activity android:name=".DetailsActivity"
+    android:exported="true"
+    <strong>android:theme="@style/Theme.Leanback"/&gt;</strong>
+
+&lt;/application&gt;
+</pre>
+
+
+<h3 id="item-listener">Listener for Clicked Items</h3>
+
+<p>After you have implemented the {@code DetailsFragment}, you must modify your main media
+  browsing view to move to your details view when a user clicks on a media item. In order to enable
+  this behavior, add an {@code OnItemClickedListener} object to the BrowseFragment that fires an
+  intent to start the item details activity.</p>
+
+<p>The following example shows how to implement a listener to start the details view when a user
+  clicks a media item in the main media browsing activity:</p>
+
+<pre>
+public class BrowseMediaActivity extends Activity {
+    ...
+
+    &#64;Override
+    protected void onCreate(Bundle savedInstanceState) {
+        ...
+
+        // create the media item rows
+        buildRowsAdapter();
+
+        // add a listener for selected items
+        mBrowseFragment.setOnItemClickedListener(
+            new OnItemClickedListener() {
+                &#64;Override
+                public void onItemClicked(Object item, Row row) {
+                    System.out.println("Media Item clicked: " + item.toString());
+                    Intent intent = new Intent(BrowseMediaActivity.this,
+                            DetailsActivity.class);
+                    // pass the item information
+                    intent.getExtras().putLong("id", item.getId());
+                    startActivity(intent);
+                }
+            });
+    }
+}
+</pre>
diff --git a/docs/html/preview/tv/ui/in-app-search.jd b/docs/html/preview/tv/ui/in-app-search.jd
new file mode 100644
index 0000000..3dbfcd2
--- /dev/null
+++ b/docs/html/preview/tv/ui/in-app-search.jd
@@ -0,0 +1,111 @@
+page.title=Adding Search to TV Apps
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#add-search-ui">Add Search User Interface</a></li>
+  </ol>
+
+</div>
+</div>
+
+
+<p>Users frequently have specific content in mind when using a media app. A search interface can
+  help your users get to the content they want faster than browsing. The Leanback library provides a
+  set of classes to enable a standard search interface within your app that is consistent with other
+  search functions on TV and provides features such as voice input.</p>
+
+<h2 id="add-search-ui">Add Search User Interface</h2>
+<p>When you use the BrowseFragment class for your media browsing interface, you can enable the
+  search icon by setting an OnClickListener to the BrowseFragment object. The following sample code
+  demonstrates this technique.</p>
+
+<pre>
+&#64;Override
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.browse_activity);
+
+    mBrowseFragment = (BrowseFragment)
+            getFragmentManager().findFragmentById(R.id.browse_fragment);
+
+    ...
+
+    mBrowseFragment.setOnSearchClickedListener(new View.OnClickListener() {
+        &#64;Override
+        public void onClick(View view) {
+            Intent intent = new Intent(BrowseActivity.this, SearchActivity.class);
+            startActivity(intent);
+        }
+    });
+
+    mBrowseFragment.setAdapter(buildAdapter());
+}
+</pre>
+
+<p class="note">
+  <strong>Note:</strong> You can set the color of the search icon using the
+  {@code setSearchAffordanceColor()} method of {@code BrowseFragment}.
+</p>
+
+<p>When a user selects the search icon, the system invokes a search activity via the defined
+  Intent. Your search activity should use a linear layout containing a SearchFragment. This fragment
+  must also implement the SearchFragment.SearchResultProvider interface in order to display the
+  results of a search. The following code sample shows how to extend the SearchFragment class to
+  provide a search interface and results:</p>
+
+<pre>
+public class MySearchFragment extends SearchFragment
+        implements SearchFragment.SearchResultProvider {
+
+    private static final int SEARCH_DELAY_MS = 300;
+    private ArrayObjectAdapter mRowsAdapter;
+    private Handler mHandler = new Handler();
+    private SearchRunnable mDelayedLoad;
+
+    &#64;Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mRowsAdapter = new ArrayObjectAdapter(new ListRowPresenter());
+        setSearchResultProvider(this);
+        setOnItemClickedListener(getDefaultItemClickedListener());
+        mDelayedLoad = new SearchRunnable();
+    }
+
+    &#64;Override
+    public ObjectAdapter getResultsAdapter() {
+        return mRowsAdapter;
+    }
+
+    &#64;Override
+    public boolean onQueryTextChange(String newQuery) {
+        mRowsAdapter.clear();
+        if (!TextUtils.isEmpty(newQuery)) {
+            mDelayedLoad.setSearchQuery(newQuery);
+            mHandler.removeCallbacks(mDelayedLoad);
+            mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
+        }
+        return true;
+    }
+
+    &#64;Override
+    public boolean onQueryTextSubmit(String query) {
+        mRowsAdapter.clear();
+        if (!TextUtils.isEmpty(query)) {
+            mDelayedLoad.setSearchQuery(query);
+            mHandler.removeCallbacks(mDelayedLoad);
+            mHandler.postDelayed(mDelayedLoad, SEARCH_DELAY_MS);
+        }
+        return true;
+    }
+}
+</pre>
+
+<p>This example code shown above is meant to be used with a separate {@code SearchRunnable}
+  class that runs the search query on a separate thread. This technique keeps potentially
+  slow-running queries from blocking the main user interface thread.</p>
+
diff --git a/docs/html/preview/tv/ui/index.jd b/docs/html/preview/tv/ui/index.jd
new file mode 100644
index 0000000..c861ec2
--- /dev/null
+++ b/docs/html/preview/tv/ui/index.jd
@@ -0,0 +1,40 @@
+page.title=User Interfaces for TV
+
+@jd:body
+
+
+<p>
+  Building an effective and engaging user interface for TV devices requires a firm understanding of what works well
+  in the context of a living room. Imagine a large screen that can be seen by many people at the
+  same time, controlled with a few buttons by users with limited attention, and you start to see the
+  challenges and opportunities of building an app for TV. Building apps for this environment
+  requires a different approach and different tools.</p>
+
+<p>This section discusses how to build a living room experience with your app, including
+  implementation instructions and creating user interface widgets built for TV. Also check out
+  <a href="{@docRoot}design/tv/index.html">Design for TV</a> for information and inspiration
+  on creating engaging user interfaces for TV devices.</p>
+
+<h2>Topics</h2>
+
+<dl>
+  <dt><b><a href="layouts.html">Layouts</a></b></dt>
+    <dd>Learn how to build app layouts for TV screens.</dd>
+
+  <dt><b><a href="navigation.html">Navigation</a></b></dt>
+    <dd>Learn how to build navigation for TV devices.</dd>
+
+  <dt><b><a href="browse.html">BrowseFragment</a></b></dt>
+    <dd>Learn how to use this fragment to build a browsing interface for media catalogs.</dd>
+
+  <dt><b><a href="details.html">DetailsFragment</a></b></dt>
+    <dd>Learn how to use this fragment to build a details page for media items.</dd>
+
+  <dt><b><a href="in-app-search.html">In-App Search</a></b></dt>
+    <dd>Learn how to use a built-for-TV user interface for searching within your app.</dd>
+
+  <dt><b><a href="recommendations.html">Recommendations</a></b></dt>
+    <dd>Learn how your app can contribute to the list of recommendations appearing on the home
+      screen and get your content noticed by users.</dd>
+</dl>
+
diff --git a/docs/html/preview/tv/ui/layouts.jd b/docs/html/preview/tv/ui/layouts.jd
new file mode 100644
index 0000000..0659826
--- /dev/null
+++ b/docs/html/preview/tv/ui/layouts.jd
@@ -0,0 +1,298 @@
+page.title=Layouts for TV
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#themes">Themes</a>
+      <ol>
+        <li><a href="#leanback-theme">Leanback Theme</a></li>
+        <li><a href="#notitle-theme">NoTitleBar Theme</a></li>
+      </ol>
+    </li>
+    <li><a href="#structure">Layout Structure</a>
+      <ol>
+        <li><a href="#overscan">Overscan</a></li>
+      </ol>
+    </li>
+    <li><a href="#visibility">Text and Controls Visibility</a></li>
+    <li><a href="#density-resources">Screen Density and Image Resources</a></li>
+    <li><a href="#anti-patterns">Layout Anti-Patterns</a></li>
+    <li><a href="#large-bitmaps">Handling Large Bitmaps</a></li>
+  </ol>
+
+</div>
+</div>
+
+<p>
+  A TV screen is typically viewed from about 10 feet away, and while it is much larger than most
+  other Android device displays, this type of screen does not provide the same level of precise
+  detail and color as a smaller device. These factors require that you create app layouts with
+  TV devices in mind in order to create a useful and enjoyable user experience.</p>
+
+<p>This guide provides direction and implementation details for building effective layouts inN
+  TV apps.</p>
+
+
+<h2 id="themes">Themes</h2>
+
+<p>Android <a href="{@docRoot}guide/topics/ui/themes.html">Themes</a> can provide a basis for
+  layouts in your TV apps. You should use a theme to modify the display of your app activities
+  that are meant to run on a TV device. This section explains which themes you should use.</p>
+
+
+<h3 id="leanback-theme">Leanback Theme</h3>
+
+<p>The Leanback library provides a standard theme for TV activities, called {@code
+  Leanback.Theme}, which establishes a consistent visual style for TV apps. Use of this theme is
+  recommended for most apps. This theme is recommended for any TV app that uses the Leanback
+  library classes. The following code sample shows how to apply this theme to a given
+  activity within an app:</p>
+
+<pre>
+&lt;activity
+  android:name="com.example.android.TvActivity"
+  android:label="&#64;string/app_name"
+  <strong>android:theme="&#64;style/Theme.Leanback"</strong>&gt;
+</pre>
+
+
+<h3 id="notitle-theme">NoTitleBar Theme</h3>
+
+<p>The title bar is a standard user interface element for Android apps on phones and tablets,
+  but it is not appropriate for TV apps. If you are not using the Leanback library classes,
+  you should apply this theme to your TV activities. The following code example from a TV app
+  manifest demonstrates how to apply this theme to remove the display of a title bar:
+</p>
+
+<pre>
+&lt;application&gt;
+  ...
+
+  &lt;activity
+    android:name="com.example.android.TvActivity"
+    android:label="&#64;string/app_name"
+    <strong>android:theme="&#64;android:style/Theme.NoTitleBar"</strong>&gt;
+    ...
+
+  &lt;/activity&gt;
+&lt;/application&gt;
+</pre>
+
+
+<h2 id="structure">Layout Structure</h2>
+
+<p>Layouts for TV devices should follow some basic guidelines to ensure they are usable and
+  effective on large screens. Follow these tips to build landscape layouts optimized for TV screens:
+</p>
+
+<ul>
+  <li>Build layouts with a landscape orientation. TV screens always display in landscape.</li>
+  <li>Put on-screen navigation controls on the left or right side of the screen and save the
+    vertical space for content.</li>
+  <li>Create UIs that are divided into sections, using <a
+    href="{@docRoot}guide/components/fragments.html"
+  >Fragments</a>, and use view groups like {@link android.widget.GridView} instead of {@link
+    android.widget.ListView} to make better use of the horizontal screen space.
+  </li>
+  <li>Use view groups such as {@link android.widget.RelativeLayout} or {@link
+    android.widget.LinearLayout} to arrange views. This approach allows the system to adjust the
+    position of the views to the size, alignment, aspect ratio, and pixel density of a TV screen.</li>
+  <li>Add sufficient margins between layout controls to avoid a cluttered UI.</li>
+</ul>
+
+
+<h3 id="overscan">Overscan</h3>
+
+<p>Layouts for TV have some unique requirements due to the evolution of TV standards and the
+  desire to always present a full screen picture to viewers. For this reason, TV devices may
+  clip the outside edge of an app layout in order to ensure that the entire display is filled.
+  This behavior is generally referred to as Overscan.</p>
+
+<p>In order to account for the impact of overscan and make sure that all the user interface
+  elements you place in a layout are actually shown on screen, you should incorporate a 10% margin
+  on all sides of your layout. This translates into a 27dp margin on the left and right edges and
+  a 48dp margin on the top and bottom of your base layouts for activities. The following
+  example layout demonstrates how to set these margins in the root layout for a TV app:
+</p>
+
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+  android:id="@+id/base_layout"
+  android:layout_width="match_parent"
+  android:layout_height="match_parent"
+  android:orientation="vertical"
+  android:layout_marginTop="27dp"
+  android:layout_marginLeft="48dp"
+  android:layout_marginRight="48dp"
+  android:layout_marginBottom="27dp" &gt;
+&lt;/LinearLayout&gt;
+</pre>
+
+<p class="caution">
+  <strong>Caution:</strong> Do not apply overscan margins to your layout if you are using the
+  Leanback Support Library {@code BrowseFragment} or related widgets, as those layouts already
+  incorporate overscan-safe margins.
+</p>
+
+
+<h2 id="visibility">Text and Controls Visibility</h2>
+
+<p>
+The text and controls in a TV app layout should be easily visible and navigable from a distance.
+Follow these tips to make them easier to see from a distance :
+</p>
+
+<ul>
+  <li>Break text into small chunks that users can quickly scan.</li>
+  <li>Use light text on a dark background. This style is easier to read on a TV.</li>
+  <li>Avoid lightweight fonts or fonts that have both very narrow and very broad strokes.
+  Use simple sans-serif fonts and anti-aliasing to increase readability.</li>
+  <li>Use Android's standard font sizes:
+<pre>
+&lt;TextView
+      android:id="@+id/atext"
+      android:layout_width="wrap_content"
+      android:layout_height="wrap_content"
+      android:gravity="center_vertical"
+      android:singleLine="true"
+      android:textAppearance="?android:attr/textAppearanceMedium"/&gt;
+</pre>
+  </li>
+  <li>Ensure that all your view widgets are large enough to be clearly visible to someone
+    sitting 10 feet away from the screen (this distance is greater for very large screens). The
+    best way to do this is to use layout-relative sizing rather than absolute sizing, and
+    density-independent pixel units instead of absolute pixel units. For example, to set the
+    width of a widget, use wrap_content instead of a pixel measurement, and to set the margin
+    for a widget, use dip instead of px values.</li>
+</ul>
+
+
+<h2 id="density-resources">Screen Density and Image Resources</h2>
+
+<p>The common high-definition TV display resolutions are 720p, 1080i, and 1080p.
+  Your TV layout should target a screen size of 1920 x 1080 pixels, and then allow the Android
+  system to downscale your layout elements to 720p if necessary. In general, downscaling
+  (removing pixels) does not degrade your layout presentation quality. However, upscaling can
+  cause display artifacts that degrade the quality of your layout and have a negative impact on
+  the user experience of your app.</p>
+
+<p>
+  To get the best scaling results for images, provide them as
+  <a href="{@docRoot}tools/help/draw9patch.html">9-patch image</a> elements if possible. If you
+  provide low quality or small images in your layouts, they will appear pixelated, fuzzy, or
+  grainy. This is not a good experience for the user. Instead, use high-quality images.
+</p>
+
+<p>
+  For more information on optimizing layouts and resources for large screens see
+  <a href="{@docRoot}training/multiscreen/index.html">Designing for multiple screens</a>.
+</p>
+
+
+<h2 id="anti-patterns">Layout Anti-Patterns</h2>
+
+<p>There are a few approaches to building layouts for TV that you should avoid because they do not
+work well and lead to bad user experiences. Here are some user interface approaches you
+should specifically <em>not</em> use when developing a layout for TV.
+</p>
+
+<ul>
+  <li><strong>Re-using phone or tablet layouts</strong> - Do not reuse layouts from a phone or
+    tablet app without modification. Layouts built for other Android device form factors are not
+    well suited for TV devices and should be simplified for operation on a TV.</li>
+  <li><strong>ActionBar</strong> - While this user interface convention is recommended for use
+    on phones and tablets, it is not appropriate for a TV interface. In particular, using an
+    action bar options menu (or any pull-down menu for that matter) is strongly discouraged, due
+    to the difficulty in navigating such a menu with a remote control.</li>
+  <li><strong>ViewPager</strong> - Sliding between screens can work great on a phone or tablet,
+    but don't try this on a TV!</li>
+
+</ul>
+
+<p>For more information on designing layouts that are appropriate to TV, see the
+  <a href="{@docRoot}design/tv/index.html">TV Design</a> guide.</p>
+
+
+<h2 id="large-bitmaps">Handling Large Bitmaps</h2>
+
+<p>TV devices, like any other Android device, have a limited amount of memory. If you build your
+  app layout with very high-resolution images or use many high-resolution images in the operation
+  of your app, it can quickly run into memory limits and cause out of memory errors.
+  To avoid these types of problems, follow these tips:</p>
+
+<ul>
+  <li>Load images only when they're displayed on the screen. For example, when displaying multiple images in
+      a {@link android.widget.GridView} or
+      {@link android.widget.Gallery}, only load an image when
+      {@link android.widget.Adapter#getView(int, View, ViewGroup) getView()}
+      is called on the View's {@link android.widget.Adapter}.
+  </li>
+  <li>Call {@link android.graphics.Bitmap#recycle()} on
+      {@link android.graphics.Bitmap} views that are no longer needed.
+  </li>
+  <li>Use {@link java.lang.ref.WeakReference} for storing references
+      to {@link android.graphics.Bitmap} objects in an in-memory
+      {@link java.util.Collection}.</li>
+  <li>If you fetch images from the network, use {@link android.os.AsyncTask}
+      to fetch and store them on the device for faster access.
+      Never do network transactions on the application's UI thread.
+  </li>
+  <li>Scale down large images to a more appropriate size as you download them;
+    otherwise, downloading the image itself may cause an out of memory exception.
+    The following sample code demonstrates how to scale down images while downloading:
+<pre>
+  // Get the source image's dimensions
+  BitmapFactory.Options options = new BitmapFactory.Options();
+  // This does not download the actual image, just downloads headers.
+  options.inJustDecodeBounds = true;
+  BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
+  // The actual width of the image.
+  int srcWidth = options.outWidth;
+  // The actual height of the image.
+  int srcHeight = options.outHeight;
+
+  // Only scale if the source is bigger than the width of the destination view.
+  if(desiredWidth > srcWidth)
+    desiredWidth = srcWidth;
+
+  // Calculate the correct inSampleSize/scale value. This approach helps reduce
+  // memory use. This value should be a power of 2.
+  int inSampleSize = 1;
+  while(srcWidth / 2 > desiredWidth){
+    srcWidth /= 2;
+    srcHeight /= 2;
+    inSampleSize *= 2;
+  }
+
+  float desiredScale = (float) desiredWidth / srcWidth;
+
+  // Decode with inSampleSize
+  options.inJustDecodeBounds = false;
+  options.inDither = false;
+  options.inSampleSize = inSampleSize;
+  options.inScaled = false;
+  // Ensures the image stays as a 32-bit ARGB_8888 image.
+  // This preserves image quality.
+  options.inPreferredConfig = Bitmap.Config.ARGB_8888;
+
+  Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
+
+  // Resize
+  Matrix matrix = new Matrix();
+  matrix.postScale(desiredScale, desiredScale);
+  Bitmap scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0,
+      sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
+  sampledSrcBitmap = null;
+
+  // Save
+  FileOutputStream out = new FileOutputStream(LOCAL_PATH_TO_STORE_IMAGE);
+  scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
+  scaledBitmap = null;
+</pre>
+  </li>
+</ul>
+
diff --git a/docs/html/preview/tv/ui/navigation.jd b/docs/html/preview/tv/ui/navigation.jd
new file mode 100644
index 0000000..92b34cf
--- /dev/null
+++ b/docs/html/preview/tv/ui/navigation.jd
@@ -0,0 +1,136 @@
+page.title=Navigation for TV
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#d-pad-navigation">D-pad Navigation</a></li>
+    <li><a href="#focus-selection">Focus and Selection</a></li>
+  </ol>
+
+</div>
+</div>
+
+<p>TV devices provide a limited set of navigation controls for apps. Creating an effective
+  navigation scheme for your TV app depends on understanding these limited controls and the limits
+  of users' perception while operating your app. As you build your Android app for TVs,
+  you should pay special attention to how the user actually navigates around your app
+  when using remote control buttons instead of a touch screen.</p>
+
+<p>This guide shows you how to build an effective navigation scheme for your TV app.</p>
+
+
+<h2 id="d-pad-navigation">D-pad Navigation</h2>
+
+<p>On a TV device, users navigate with controls on a remote control device, using either a
+  directional pad (D-pad) or arrow keys. This type of control limits movement to up, down, left,
+  and right. To build a great TV-optimized app, you must provide a navigation scheme where
+  the user can quickly learn how to navigate your app using these limited controls.</p>
+
+<p>Follow these guidelines to build a navigation system that works well with a D-pad on a TV device:
+</p>
+
+<ul>
+  <li>Ensure that the D-pad can navigate to all the visible controls on the screen.</li>
+  <li>For scrolling lists with focus, D-pad up/down keys scroll the list, and the Enter key selects
+    an item in the list. Ensure that users can select an element in the list and that the list still
+    scrolls when an element is selected.</li>
+  <li>Ensure that movement between controls is straightforward and predictable.</li>
+</ul>
+
+<p>The Android framework handles directional navigation between layout elements automatically, so
+  you typically do not need to do anything extra for your app. However, you should thoroughly test
+  navigation with a D-pad control to discover any navigation problems. If you discover that your
+  screen layout makes navigation difficult, or if you want users to move through the layout in a
+  specific way, you can set up explicit directional navigation for your controls. The following
+  code sample shows how to define the next control to receive focus for a
+  {@link android.widget.TextView} layout object:</p>
+
+<pre>
+&lt;TextView android:id="&#64;+id/Category1"
+             android:nextFocusDown="&#64;+id/Category2"\&gt;
+</pre>
+
+<p>The following table lists all of the available navigation attributes for Android user interface
+widgets:</p>
+
+<table>
+  <tr>
+    <th>Attribute</th>
+    <th>Function</th>
+  </tr>
+  <tr>
+    <td>{@link android.R.attr#nextFocusDown}</td>
+    <td>Defines the next view to receive focus when the user navigates down.</td>
+  </tr>
+  <tr>
+    <td>{@link android.R.attr#nextFocusLeft}</td>
+    <td>Defines the next view to receive focus when the user navigates left.</td>
+  </tr>
+  <tr>
+    <td>{@link android.R.attr#nextFocusRight}</td>
+    <td>Defines the next view to receive focus when the user navigates right.</td>
+  </tr>
+  <tr>
+    <td>{@link android.R.attr#nextFocusUp}</td>
+    <td>Defines the next view to receive focus when the user navigates up.</td>
+  </tr>
+</table>
+
+<p>To use one of these explicit navigation attributes, set the value to the ID ({@code android:id}
+  value) of another widget in the layout. You should set up the navigation order as a loop, so that
+  the last control directs focus back to the first one.</p>
+
+<p class="note">
+  <strong>Note:</strong> You should only use these attributes to modify the navigation order if the
+  default order that the system applies does not work well.
+</p>
+
+
+<h2 id="focus-selection">Focus and Selection</h2>
+
+<p>The success of a navigation scheme on TV devices is strongly dependent on how easy it is for a
+  user to determine what user interface element is in focus on screen. If you do not provide clear
+  indications of what is in focus on screen (and therefore what item they can take action on),
+  users can quickly become frustrated and exit your app. By the same token, it is important
+  to always have an item in focus that a user can take action on immediately after your app starts,
+  and any time your app is not playing content.</p>
+
+<p>Your app layout and implementation should use color, size, animation, or a combination of
+  these attributes to help users easily determine what actions they can take next. Use a uniform
+  scheme for indicating focus across your application.</p>
+
+<p>Android provides <a href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">
+Drawable State List Resources</a> to implement highlights for selected and focused controls. The
+following code example demonstates how to indicate selection of a button object:
+</p>
+
+<pre>
+&lt;!-- res/drawable/button.xml --&gt;
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;selector xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+    &lt;item android:state_pressed="true"
+          android:drawable="@drawable/button_pressed" /&gt; &lt;!-- pressed --&gt;
+    &lt;item android:state_focused="true"
+          android:drawable="@drawable/button_focused" /&gt; &lt;!-- focused --&gt;
+    &lt;item android:state_hovered="true"
+          android:drawable="@drawable/button_focused" /&gt; &lt;!-- hovered --&gt;
+    &lt;item android:drawable="@drawable/button_normal" /&gt; &lt;!-- default --&gt;
+&lt;/selector&gt;
+</pre>
+
+<p>
+This layout XML applies the above state list drawable to a {@link android.widget.Button}:
+</p>
+<pre>
+&lt;Button
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:background="@drawable/button" /&gt;
+</pre>
+
+<p>Make sure to provide sufficient padding within the focusable and selectable controls so that
+  the highlights around them are clearly visible.</p>
+
diff --git a/docs/html/preview/tv/ui/recommendations.jd b/docs/html/preview/tv/ui/recommendations.jd
new file mode 100644
index 0000000..2c78064
--- /dev/null
+++ b/docs/html/preview/tv/ui/recommendations.jd
@@ -0,0 +1,209 @@
+page.title=Making Recommendations
+
+@jd:body
+
+<div id="qv-wrapper">
+<div id="qv">
+  <h2>In this document</h2>
+  <ol>
+    <li><a href="#service">Create a Recommendations Service</a></li>
+    <li><a href="#build">Build Recommendations</a></li>
+    <li><a href="#run-service">Run Recommendations Service</a></li>
+    <li><a href="#DesignLandscapeLayouts">Design Landscape Layouts</a></li>
+  </ol>
+
+</div>
+</div>
+
+
+<p>Content recommendations appear as the first row of the TV launch screen after the first use
+  of the device. This row is intended to help users quickly find content they enjoy. Contributing
+  recommendations from your apps content catalog can help bring users back to your app.</p>
+
+
+<img src="{@docRoot}preview/tv/images/home-recommendations.png" alt="" id="figure1" />
+<p class="img-caption">
+  <strong>Figure 1.</strong> An example of the recommendations row.
+</p>
+
+
+<h2 id="service">Create a Recommendations Service</h2>
+
+<p>Content recommendations are created with background processing. In order for your application
+  to contribute to recommendations, you create a service that periodically adds listings from your
+  app's catalog to the system list of recommendations.</p>
+
+<p>The following code example illustrates how to extend the {@link android.app.IntentService} to
+  create a recommendation service for your application.</p>
+
+<pre>
+public class RecommendationsService extends IntentService {
+    private static final int MAX_RECOMMENDATIONS = 3;
+
+    public RecommendationsService() {
+        super("RecommendationService");
+    }
+
+    &#64;Override
+    protected void onHandleIntent(Intent intent) {
+        MovieDatabase database = MovieDatabase.instance(getApplicationContext());
+        List<Movie> recommendations = database.recommendations();
+
+        int count = 0;
+
+        try {
+            for (Movie movie : recommendations) {
+                // build the individual content recommendations
+                buildRecommendation(getApplicationContext(), movie);
+
+                if (++count >= MAX_RECOMMENDATIONS) {
+                    break;
+                }
+            }
+        } catch (IOException e) {
+            Log.e(TAG, "Unable to update recommendation", e);
+        }
+    }
+}
+</pre>
+
+<p>In order for this class to be recognized and run as a service, you must register this service
+  using your app manifest. The following code snippet illustrates how to add this class as a
+  service:</p>
+
+<pre>
+&lt;manifest ... &gt;
+  &lt;application ... &gt;
+    ...
+
+    &lt;service android:name=&quot;.RecommendationsService&quot;
+             android:enabled=&quot;true&quot; android:exported=&quot;true&quot;/&gt;
+  &lt;/application&gt;
+&lt;/manifest&gt;
+</pre>
+
+<h2 id="build">Build Recommendations</h2>
+
+<p>Once it starts running, your service must create recommendations and pass them to the Android
+  framework. The framework receives the recommendations as {@link android.app.Notification} objects
+  that use a specific style and are marked with a specific category.</p>
+
+<p>The following code example demonstrates how to get an instance of the {@link
+  android.app.NotificationManager}, build a recommendation, and post it to the manager:</p>
+
+<pre>
+public class RecommendationsService extends IntentService {
+
+    ...
+
+    public Notification buildRecommendation(Context context, Movie movie)
+            throws IOException {
+
+        if (mNotificationManager == null) {
+            mNotificationManager = (NotificationManager)
+                    mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+        }
+
+        Bundle extras = new Bundle();
+        if (mBackgroundUri != movie.getBackgroundUri()) {
+            extras.putString(EXTRA_BACKGROUND_IMAGE_URL, movie.getBackgroundUri());
+        }
+
+        // build the recommendation as a Notification object
+        Notification notification = new NotificationCompat.BigPictureStyle(
+                new NotificationCompat.Builder(context)
+                        .setContentTitle(movie.getTitle())
+                        .setContentText(movie.getDescription())
+                        .setPriority(movie.getPriority())
+                        .setOngoing(true)
+                        .setCategory("recommendation")
+                        .setLargeIcon(movie.getImage())
+                        .setSmallIcon(movie.getSmallIcon())
+                        .setContentIntent(buildPendingIntent(movie.getId()))
+                        .setExtras(extras))
+                .build();
+
+        // post the recommendation to the NotificationManager
+        mNotificationManager.notify(movie.getId(), notification);
+        mNotificationManager = null;
+        return notification;
+    }
+
+    private PendingIntent buildPendingIntent(long id) {
+        Intent detailsIntent = new Intent(this, DetailsActivity.class);
+        detailsIntent.putExtra("id", id);
+
+        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
+        stackBuilder.addParentStack(DetailsActivity.class);
+        stackBuilder.addNextIntent(detailsIntent);
+        // Ensure each PendingIntent is unique
+        detailsIntent.setAction(Long.toString(id));
+
+        PendingIntent intent = stackBuilder.getPendingIntent(
+                0, PendingIntent.FLAG_UPDATE_CURRENT);
+        return intent;
+    }
+}
+</pre>
+
+
+<h3 id="run-service">Run Recommendations Service</h3>
+
+<p>Your app's recommendation service must run periodically in order to create current
+  recommendations. In order to run your service, you should create a class that runs a timer and
+  invokes it at regular intervals. The following code example extends the {@link
+  android.content.BroadcastReceiver} class to start periodic execution of a recommendation service
+  every 12 hours:</p>
+
+<pre>
+public class BootupReceiver extends BroadcastReceiver {
+    private static final String TAG = "BootupActivity";
+
+    private static final long INITIAL_DELAY = 5000;
+
+    &#64;Override
+    public void onReceive(Context context, Intent intent) {
+        if (intent.getAction().endsWith(Intent.ACTION_BOOT_COMPLETED)) {
+            scheduleRecommendationUpdate(context);
+        }
+    }
+
+    private void scheduleRecommendationUpdate(Context context) {
+        AlarmManager alarmManager = (AlarmManager)context.getSystemService(
+                Context.ALARM_SERVICE);
+        Intent recommendationIntent = new Intent(context,
+                UpdateRecommendationsService.class);
+        PendingIntent alarmIntent = PendingIntent.getService(context, 0,
+                recommendationIntent, 0);
+
+        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+                INITIAL_DELAY,
+                AlarmManager.INTERVAL_HALF_DAY,
+                alarmIntent);
+    }
+}
+</pre>
+
+<p>In order for the {@link android.content.BroadcastReceiver} class to execute after a TV
+  device starts up, you must register this class in your app manifest and attach an intent filter
+  in order for the device boot process to complete. This sample code demonstrates how to add this
+  configuration to the manifest:</p>
+
+<pre>
+&lt;manifest ... &gt;
+  &lt;application ... &gt;
+    &lt;receiver android:name=&quot;.BootupReceiver&quot; android:enabled=&quot;true&quot;
+              android:exported=&quot;false&quot;&gt;
+      &lt;intent-filter&gt;
+        &lt;action android:name=&quot;android.intent.action.BOOT_COMPLETED&quot;/&gt;
+      &lt;/intent-filter&gt;
+    &lt;/receiver&gt;
+  &lt;/application&gt;
+&lt;/manifest&gt;
+</pre>
+
+<p class="important">
+  <strong>Important:</strong> Receiving a boot completed notification requires that your app
+  request the {@link android.Manifest.permission#RECEIVE_BOOT_COMPLETED} permission.
+  For more information, see {@link android.content.Intent#ACTION_BOOT_COMPLETED}.
+</p>
diff --git a/docs/html/sdk/installing/create-project.jd b/docs/html/sdk/installing/create-project.jd
new file mode 100644
index 0000000..c0d523a
--- /dev/null
+++ b/docs/html/sdk/installing/create-project.jd
@@ -0,0 +1,86 @@
+page.title=Creating a Project
+
+@jd:body
+
+<p>Android Studio makes it easy to create Android apps for several form factors, such as phone,
+tablet, TV, Wear, and Google Glass. The <em>New Project</em> wizard lets you choose the form factors
+for your app and populates the project structure with everything you need to get started.</p>
+
+<p>Follow the steps in this section to create a project in Android Studio.</p>
+
+<h2>Step 1: Create a New Project</h2>
+
+<p>If you didn't have a project opened, Android Studio shows the Welcome screen.
+To create a new project, click <strong>New Project</strong>.</p>
+
+<p>If you had a project opened, Android Studio shows the development environment.
+To create a new project, click <strong>File</strong> > <strong>New Project</strong>.</p>
+
+<p>The next window lets you configure the name of your app, the package name, and the location
+of your project.</p>
+
+<img src="{@docRoot}images/tools/wizard2.png" alt="" width="500" height="381">
+<p class="img-caption"><strong>Figure 1.</strong> Choose a name for your project.</p>
+
+<p>Enter the values for your project and click <strong>Next</strong>.</p>
+
+
+<h2>Step 2: Select Form Factors and API Level</h2>
+
+<p>The next window lets you select the form factors supported by your app, such as phone, tablet,
+TV, Wear, and Google Glass. For each form factor, you can also select the API
+Level that your app requires. To get more information, click <strong>Help me choose</strong>.</p>
+
+<img src="{@docRoot}images/tools/wizard4.png" alt="" width="750" height="510">
+<p class="img-caption"><strong>Figure 2.</strong> Select the API Level.</p>
+
+<p>The API Level window shows the distribution of mobile devices running each version of Android,
+as shown in Figure 2. Click on an API level to see a list of features introduced in the corresponding
+version of Android. This helps you choose the minimum API Level that has all the features that
+your apps needs, so you can reach as many devices as possible. Then click <strong>OK</strong>.</p>
+
+<img src="{@docRoot}images/tools/wizard3.png" alt="" width="500" height="480">
+<p class="img-caption"><strong>Figure 3.</strong> Choose form factors for your app.</p>
+
+<p>Then, on the Form Factors Window, click <strong>Next</strong>.</p>
+
+
+<h2>Step 3: Add an Activity</h2>
+
+<p>The next screen lets you select an activity type to add to your app, as shown in Figure 4.
+This screen depends on the form factors you selected earlier.</p>
+
+<img src="{@docRoot}images/tools/wizard5.png" alt="" width="720" height="504">
+<p class="img-caption"><strong>Figure 4.</strong> Add an activity to your app.</p>
+
+<p>Choose an activity type and click <strong>Next</strong>.</p>
+
+
+<h2>Step 4: Configure Your Activity</h2>
+
+<p>The next screen lets you configure the activity to add to your app, as shown in Figure 6.</p>
+
+<img src="{@docRoot}images/tools/wizard6.png" alt="" width="450" height="385">
+<p class="img-caption"><strong>Figure 6.</strong> Choose a name for your activity.</p>
+
+<p>Enter the activity name, the layout name, and the activity title. Then click
+<strong>Finish</strong>.</p>
+
+
+<h2>Step 5: Develop Your App</h2>
+
+<p>Android Studio creates the default structure for your project and opens the development
+environment. If your app supports more than one form factor, Android Studio creates a module for
+each of them, as shown in Figure 7.</p>
+
+<img src="{@docRoot}images/tools/wizard7.png" alt="" width="750" height="509">
+<p class="img-caption"><strong>Figure 7.</strong> The default project structure for a mobile app.</p>
+
+<p>Now you are ready to develop your app. Fore more information, see the following links:</p>
+
+<ul>
+<li><a href="{@docRoot}training/">Training Lessons</a></li>
+<li><a href="{@docRoot}training/building-wearables.html">Building Apps for Wearables</a></li>
+<li><a href="{@docRoot}tv/">Android TV</a></li>
+<li><a href="https://developers.google.com/glass/">Google Glass</a></li>
+</ul>
diff --git a/docs/html/sdk/installing/index.jd b/docs/html/sdk/installing/index.jd
index 3671726..304b53d 100644
--- a/docs/html/sdk/installing/index.jd
+++ b/docs/html/sdk/installing/index.jd
@@ -3,8 +3,8 @@
 @jd:body
 
 <style>
-.paging-links {
-  margin:0 0 80px;
+p.paging-links {
+  margin:0 0 40px;
 }
 .paging-links .next-page-link {
   right:initial;
@@ -14,6 +14,10 @@
   margin-bottom:1em;
   background:#eee;
 }
+.landing-button {
+  min-width: 155px;
+  text-align: center;
+}
 </style>
 
 
@@ -316,39 +320,32 @@
 
 
 <!-- ################    DEFAULT    ##################### -->
-<style>
-h3.large-link {
-  display:inline-block;
-  width:100%;
-  text-align:center;
-  border:1px solid #258aaf;
-  padding:10px 0;
-  color:inherit;
-}
-</style>
+
 
 <div id="default" style="display:none">
 
-<p>If you haven't already done so, <b><a href="{@docRoot}sdk/index.html">click here to download
+<p>If you haven't already, <b><a href="{@docRoot}sdk/index.html">download
 the Android SDK</a></b>. </p>
 
-<p>Otherwise, select which SDK package you want to install:</p>
+<p>Then, select which SDK package you want to install:</p>
 
-<div class="cols" style="margin-bottom:60px">
-<div class="col-4">
-<a href="{@docRoot}sdk/installing/index.html?pkg=adt">
-<h3 class="large-link">Eclipse ADT</h3>
+<div class="cols" style="margin:30px 0 60px">
+<div class="col-4" style="margin-left:0">
+<a href="{@docRoot}sdk/installing/index.html?pkg=adt" class="landing-button landing-secondary">
+Eclipse ADT
 </a>
 </div>
 
 <div class="col-4">
-<a href="{@docRoot}sdk/installing/index.html?pkg=studio">
-<h3 class="large-link">Android Studio</h3></a>
+<a href="{@docRoot}sdk/installing/index.html?pkg=studio" class="landing-button landing-secondary">
+Android Studio
+</a>
 </div>
 
 <div class="col-4">
-<a href="{@docRoot}sdk/installing/index.html?pkg=tools">
-<h3 class="large-link">Stand-alone SDK Tools</h3></a>
+<a href="{@docRoot}sdk/installing/index.html?pkg=tools" class="landing-button landing-secondary">
+Stand-alone SDK Tools
+</a>
 </div>
 </div>
 
diff --git a/docs/html/sdk/installing/studio.jd b/docs/html/sdk/installing/studio.jd
index 8ac6163..894514a 100644
--- a/docs/html/sdk/installing/studio.jd
+++ b/docs/html/sdk/installing/studio.jd
@@ -186,7 +186,7 @@
 <div id="main">
 
 <div class="figure" style="width:400px;margin-top:-75px">
-<img src="{@docRoot}images/tools/android-studio.png" height="330" width="400" style="margin-bottom:20px" />
+<img src="{@docRoot}images/tools/laptop-studio.png" height="366" width="400" style="margin-bottom:20px" />
 
 <a class="big button subtitle" id="download-ide-button"
 href="" style="display:none;width:368px;margin:0 auto;display:block;font-size:18px" ></a>
@@ -218,7 +218,7 @@
   <li>Lint tools to catch performance, usability, version compatibility, and other problems.</li>
   <li>ProGuard and app-signing capabilities.</li>
   <li>Built-in support for <a
-  href="http://android-developers.blogspot.com/2013/06/adding-backend-to-your-app-in-android.html"
+  href="https://developers.google.com/cloud/devtools/android_studio_templates/"
   class="external-link">Google Cloud Platform</a>, making it easy to integrate Google Cloud
   Messaging and App Engine.
 </ul>
diff --git a/docs/html/tools/help/adb.jd b/docs/html/tools/help/adb.jd
index f980042..e2dd196 100644
--- a/docs/html/tools/help/adb.jd
+++ b/docs/html/tools/help/adb.jd
@@ -8,6 +8,7 @@
 <div id="qv">
   <h2>In this document</h2>
 <ol>
+  <li><a href="#Enabling">Enabling adb Debugging</a></li>
   <li><a href="#issuingcommands">Syntax</a></li>
   <li><a href="#commandsummary">Commands</a></li>
   <li><a href="#devicestatus">Querying for Emulator/Device Instances</a></li>
@@ -72,6 +73,19 @@
 instance from any client (or from a script).</p>
 
 
+<h2 id="Enabling">Enabling adb Debugging</h2>
+
+<p>In order to use adb with a device connected over USB, you must enable
+<strong>USB debugging</strong> in the device system settings, under <strong>
+Developer options</strong>.</p>
+
+<p>On Android 4.2 and higher, the Developer options screen is
+hidden by default. To make it visible, go to
+<b>Settings &gt; About phone</b> and tap <b>Build number</b> seven times. Return to the previous
+screen to find <strong>Developer options</strong> at the bottom.</p>
+
+<p>On some devices, the Developer options screen may be located or named differently.</p>
+
 <p class="note"><strong>Note:</strong> When you connect a device running Android 4.2.2 or higher
 to your computer, the system shows a dialog asking whether to accept an RSA key that allows
 debugging through this computer. This security mechanism protects user devices because it ensures
@@ -80,6 +94,11 @@
 SDK Platform-tools r16.0.1 and higher) in order to debug on a device running Android 4.2.2 or
 higher.</p>
 
+<p>For more information about connecting to a device over USB, read
+<a href="{@docRoot}tools/device.html">Using Hardware Devices</a>.</p>
+
+
+
 
 <h2 id="issuingcommands">Syntax</h2>
 
diff --git a/docs/html/tools/tools_toc.cs b/docs/html/tools/tools_toc.cs
index fb4659f..93e5976 100644
--- a/docs/html/tools/tools_toc.cs
+++ b/docs/html/tools/tools_toc.cs
@@ -11,7 +11,7 @@
           <span class="en">Adding SDK Packages</span></a></li>
     </ul>
   </li>
-  
+
 
   <li class="nav-section">
     <div class="nav-section-header">
@@ -20,6 +20,8 @@
     <ul>
       <li><a href="<?cs var:toroot ?>sdk/installing/migrate.html">
           Migrating from Eclipse</a></li>
+      <li><a href="<?cs var:toroot ?>sdk/installing/create-project.html">
+          Creating a Project</a></li>
       <li><a href="<?cs var:toroot ?>sdk/installing/studio-tips.html">
           Tips and Tricks</a></li>
       <li><a href="<?cs var:toroot ?>sdk/installing/studio-layout.html">
diff --git a/docs/html/training/app-indexing/index.jd b/docs/html/training/app-indexing/index.jd
index cb4135f..7e7241b 100644
--- a/docs/html/training/app-indexing/index.jd
+++ b/docs/html/training/app-indexing/index.jd
@@ -59,14 +59,6 @@
 </li>
 </ol>
 
-<p class="note"><strong>Note: </strong>
-Currently, the Google Search app indexing capability is restricted to
-English-only Android apps from developers participating in the early adopter
-program. You can sign up to be a participant by submitting the <a
-href="https://docs.google.com/a/google.com/forms/d/1itcqPAQqggJ6e4m8aejWLM8Dc5O8P6qybgGbKCNxGV0/viewform"
-class="external-link" target="_blank">App Indexing Expression of Interest</a> form.
-</p>
-
 <p>This class shows how to enable deep linking and indexing of your application
 content so that users can open this content directly from mobile search
 results.</p>
diff --git a/docs/html/training/tv/index.jd b/docs/html/training/tv/index.jd
new file mode 100644
index 0000000..54f7016
--- /dev/null
+++ b/docs/html/training/tv/index.jd
@@ -0,0 +1,59 @@
+page.title=Designing for TV
+page.tags="input","screens"
+
+trainingnavtop=true
+startpage=true
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<!-- Required platform, tools, add-ons, devices, knowledge, etc. -->
+<h2>Dependencies and prerequisites</h2>
+<ul>
+  <li>Android 2.0 (API Level 5) or higher</li>
+</ul>
+
+</div>
+</div>
+
+<a class="notice-developers-video wide" href="http://www.youtube.com/watch?v=zsRnRLh-O34">
+<div>
+    <h3>Video</h3>
+    <p>DevBytes: Design for Large Displays - Part 1</p>
+</div>
+</a>
+
+<p> 
+  Smart TVs powered by Android bring your favorite Android apps to the best screen in your house. 
+  Thousands of apps in the Google Play Store are already optimized for TVs. This class shows how 
+  you can optimize your Android app for TVs, including how to build a layout that 
+  works great when the user is ten feet away and navigating with a remote control. 
+</p> 
+
+<h2>Lessons</h2> 
+ 
+<dl> 
+  <dt><b><a href="optimizing-layouts-tv.html">Optimizing Layouts for TV</a></b></dt>
+    <dd>Shows you how to optimize app layouts for TV screens, which have some unique characteristics such as:
+    <ul>
+      <li>permanent "landscape" mode</li>
+      <li>high-resolution displays</li>
+      <li>"10 foot UI" environment.</li>
+    </ul>
+    </dd>
+ 
+  <dt><b><a href="optimizing-navigation-tv.html">Optimizing Navigation for TV</a></b></dt>
+    <dd>Shows you how to design navigation for TVs, including: 
+    <ul>
+      <li>handling D-pad navigation</li>
+      <li>providing navigational feedback</li>
+      <li>providing easily-accessible controls on the screen.</li>
+    </ul>
+    </dd>
+
+  <dt><b><a href="unsupported-features-tv.html">Handling features not supported on TV</a></b></dt>
+    <dd>Lists the hardware features that are usually not available on TVs. This lesson also shows you how to 
+    provide alternatives for missing features or check for missing features and disable code at run time.</dd>
+</dl> 
diff --git a/docs/html/training/tv/optimizing-layouts-tv.jd b/docs/html/training/tv/optimizing-layouts-tv.jd
new file mode 100644
index 0000000..a6db052
--- /dev/null
+++ b/docs/html/training/tv/optimizing-layouts-tv.jd
@@ -0,0 +1,246 @@
+page.title=Optimizing Layouts for TV
+parent.title=Designing for TV
+parent.link=index.html
+
+trainingnavtop=true
+next.title=Optimizing Navigation for TV
+next.link=optimizing-navigation-tv.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#DesignLandscapeLayouts">Design Landscape Layouts</a></li>
+  <li><a href="#MakeTextControlsEasyToSee">Make Text and Controls Easy to See</a></li>
+  <li><a href="#DesignForLargeScreens">Design for High-Density Large Screens</a></li>
+  <li><a href="#HandleLargeBitmaps">Design to Handle Large Bitmaps</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>
+When your application is running on a television set, you should assume that the user is sitting about 
+ten feet away from the screen. This user environment is referred to as the 
+<a href="http://en.wikipedia.org/wiki/10-foot_user_interface">10-foot UI</a>. To provide your 
+users with a usable and enjoyable experience, you should style and lay out your UI accordingly..
+</p>
+<p>
+This lesson shows you how to optimize layouts for TV by:
+</p>
+<ul>
+  <li>Providing appropriate layout resources for landscape mode.</li>
+  <li>Ensuring that text and controls are large enough to be visible from a distance.</li>
+  <li>Providing high resolution bitmaps and icons for HD TV screens.</li>
+</ul>
+
+<h2 id="DesignLandscapeLayouts">Design Landscape Layouts</h2> 
+
+<p>
+TV screens are always in landscape orientation. Follow these tips to build landscape layouts optimized for TV screens:
+</p> 
+<ul>
+  <li>Put on-screen navigational controls on the left or right side of the screen and save the 
+  vertical space for content.</li>
+  <li>Create UIs that are divided into sections, by using <a href="{@docRoot}guide/components/fragments.html">Fragments</a> 
+  and use view groups like {@link android.widget.GridView} instead 
+  of {@link android.widget.ListView} to make better use of the 
+  horizontal screen space.</li>
+  <li>Use view groups such as {@link android.widget.RelativeLayout} 
+  or {@link android.widget.LinearLayout} to arrange views. 
+  This allows the Android system to adjust the position of the views to the size, alignment, 
+  aspect ratio, and pixel density of the TV screen.</li>
+  <li>Add sufficient margins between layout controls to avoid a cluttered UI.</li>
+</ul> 
+ 
+<p>
+For example, the following layout is optimized for TV:
+</p>
+
+<img src="{@docRoot}images/training/panoramio-grid.png" />
+
+<p>
+In this layout, the controls are on the lefthand side. The UI is displayed within a 
+{@link android.widget.GridView}, which is well-suited to landscape orientation.
+In this layout both GridView and Fragment have the width and height set 
+dynamically, so they can adjust to the screen resolution. Controls are added to the left side Fragment programatically at runtime.
+The layout file for this UI is {@code res/layout-land-large/photogrid_tv.xml}.
+(This layout file is placed in {@code layout-land-large} because TVs have large screens with landscape orientation. For details refer to 
+<a href="{@docRoot}guide/practices/screens_support.html">Supporting Multiple Screens</a>.)</p>
+
+res/layout-land-large/photogrid_tv.xml
+<pre>
+&lt;RelativeLayout
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent" &gt;
+
+    &lt;fragment
+        android:id="@+id/leftsidecontrols"
+        android:layout_width="0dip"
+        android:layout_marginLeft="5dip"
+        android:layout_height="match_parent" /&gt;
+
+    &lt;GridView        
+        android:id="@+id/gridview"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" /&gt;
+
+&lt;/RelativeLayout>
+</pre>
+
+<p>
+To set up action bar items on the left side of the screen, you can also include the <a
+href="http://code.google.com/p/googletv-android-samples/source/browse/#git%2FLeftNavBarLibrary">
+Left navigation bar library</a> in your application to set up action items on the left side 
+of the screen, instead of creating a custom Fragment to add controls:
+</p>
+
+<pre>
+LeftNavBar bar = (LeftNavBarService.instance()).getLeftNavBar(this);
+</pre>
+
+<p>
+When you have an activity in which the content scrolls vertically, always use a left navigation bar; 
+otherwise, your users have to scroll to the top of the content to switch between the content view and 
+the ActionBar. Look at the  
+<a href="http://code.google.com/p/googletv-android-samples/source/browse/#git%2FLeftNavBarDemo">
+Left navigation bar sample app</a> to see how to simple it is to include the left navigation bar in your app.
+</p>
+
+<h2 id="MakeTextControlsEasyToSee">Make Text and Controls Easy to See</h2>
+<p>
+The text and controls in a TV application's UI should be easily visible and navigable from a distance.
+Follow these tips to make them easier to see from a distance :
+</p>
+
+<ul>
+  <li>Break text into small chunks that users can quickly scan.</li>
+  <li>Use light text on a dark background. This style is easier to read on a TV.</li>
+  <li>Avoid lightweight fonts or fonts that have both very narrow and very broad strokes. Use simple sans-serif 
+  fonts and use anti-aliasing to increase readability.</li>
+  <li>Use Android's standard font sizes:
+  <pre>
+  &lt;TextView
+        android:id="@+id/atext"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:gravity="center_vertical"
+        android:singleLine="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"/&gt;
+  </pre></li>
+  <li>Ensure that all your view widgets are large enough to be clearly visible to someone sitting 10 feet away 
+  from the screen (this distance is greater for very large screens).  The best way to do this is to use 
+  layout-relative sizing rather than absolute sizing, and density-independent pixel units instead of absolute 
+  pixel units. For example, to set the width of a widget, use wrap_content instead of a pixel measurement, 
+  and to set the margin for a widget, use dip instead of px values.
+  </li>
+</ul>
+<p>
+
+</p>
+
+<h2 id="DesignForLargeScreens">Design for High-Density Large Screens</h2>
+
+<p>
+The common HDTV display resolutions are 720p, 1080i, and 1080p. Design your UI for 1080p, and then 
+allow the Android system to downscale your UI to 720p if necessary. In general, downscaling (removing pixels) 
+does not degrade the UI (Notice that the converse is not true; you should avoid upscaling because it degrades 
+UI quality).
+</p>
+
+<p>
+To get the best scaling results for images, provide them as <a href="{@docRoot}tools/help/draw9patch.html">
+9-patch image</a> elements if possible.
+If you provide low quality or small images in your layouts, they will appear pixelated, fuzzy, or grainy. This 
+is not a good experience for the user. Instead, use high-quality images. 
+</p>
+
+<p>
+For more information on optimizing apps for large screens see <a href="{@docRoot}training/multiscreen/index.html">
+Designing for multiple screens</a>.
+</p>
+
+<h2 id="HandleLargeBitmaps">Design to Handle Large Bitmaps</h2>
+
+<p>
+The Android system has a limited amount of memory, so downloading and storing high-resolution images can often 
+cause out-of-memory errors in your app. To avoid this, follow these tips:
+</p>
+
+<ul>
+  <li>Load images only when they're displayed on the screen. For example, when displaying multiple images in 
+      a {@link android.widget.GridView} or 
+      {@link android.widget.Gallery}, only load an image when 
+      {@link android.widget.Adapter#getView(int, View, ViewGroup) getView()} 
+      is called on the View's {@link android.widget.Adapter}.
+  </li>
+  <li>Call {@link android.graphics.Bitmap#recycle()} on 
+      {@link android.graphics.Bitmap} views that are no longer needed.
+  </li>
+  <li>Use {@link java.lang.ref.WeakReference} for storing references 
+      to {@link android.graphics.Bitmap} objects in an in-memory 
+      {@link java.util.Collection}.</li>
+  <li>If you fetch images from the network, use {@link android.os.AsyncTask} 
+      to fetch them and store them on the SD card for faster access.
+      Never do network transactions on the application's UI thread.
+  </li>
+  <li>Scale down really large images to a more appropriate size as you download them; otherwise, downloading the image 
+  itself may cause an "Out of Memory" exception. Here is sample code that scales down images while downloading:
+  
+  <pre>
+  // Get the source image's dimensions
+  BitmapFactory.Options options = new BitmapFactory.Options();
+  // This does not download the actual image, just downloads headers.
+  options.inJustDecodeBounds = true; 
+  BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
+  // The actual width of the image.
+  int srcWidth = options.outWidth;  
+  // The actual height of the image.
+  int srcHeight = options.outHeight;  
+
+  // Only scale if the source is bigger than the width of the destination view.
+  if(desiredWidth > srcWidth)
+    desiredWidth = srcWidth;
+
+  // Calculate the correct inSampleSize/scale value. This helps reduce memory use. It should be a power of 2.
+  int inSampleSize = 1;
+  while(srcWidth / 2 > desiredWidth){
+    srcWidth /= 2;
+    srcHeight /= 2;
+    inSampleSize *= 2;
+  }
+
+  float desiredScale = (float) desiredWidth / srcWidth;
+
+  // Decode with inSampleSize
+  options.inJustDecodeBounds = false;
+  options.inDither = false;
+  options.inSampleSize = inSampleSize;
+  options.inScaled = false;
+  // Ensures the image stays as a 32-bit ARGB_8888 image.
+  // This preserves image quality.
+  options.inPreferredConfig = Bitmap.Config.ARGB_8888;  
+                                                	
+  Bitmap sampledSrcBitmap = BitmapFactory.decodeFile(IMAGE_FILE_URL, options);
+
+  // Resize
+  Matrix matrix = new Matrix();
+  matrix.postScale(desiredScale, desiredScale);
+  Bitmap scaledBitmap = Bitmap.createBitmap(sampledSrcBitmap, 0, 0,
+      sampledSrcBitmap.getWidth(), sampledSrcBitmap.getHeight(), matrix, true);
+  sampledSrcBitmap = null;
+
+  // Save
+  FileOutputStream out = new FileOutputStream(LOCAL_PATH_TO_STORE_IMAGE);
+  scaledBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
+  scaledBitmap = null;
+   </pre>
+  </li> </ul>
\ No newline at end of file
diff --git a/docs/html/training/tv/optimizing-navigation-tv.jd b/docs/html/training/tv/optimizing-navigation-tv.jd
new file mode 100644
index 0000000..bb78258
--- /dev/null
+++ b/docs/html/training/tv/optimizing-navigation-tv.jd
@@ -0,0 +1,206 @@
+page.title=Optimizing Navigation for TV
+parent.title=Designing for TV
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Optimizing Layouts for TV
+previous.link=optimizing-layouts-tv.html
+next.title=Handling Features Not Supported on TV
+next.link=unsupported-features-tv.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#HandleDpadNavigation">Handle D-pad Navigation</a></li>
+  <li><a href="#HandleFocusSelection">Provide Clear Visual Indication for Focus and Selection</a></li>
+  <li><a href="#DesignForEasyNavigation">Design for Easy Navigation</a></li>
+</ol>
+
+<h2>You should also read</h2>
+<ul>
+  <li><a href="{@docRoot}training/design-navigation/index.html">Designing Effective Navigation</a></li>
+</ul>
+
+</div>
+</div>
+
+<p>
+An important aspect of the user experience when operating a TV is the direct human interface: a remote control. 
+As you optimize your Android application for TVs, you should pay special attention to how the user actually navigates 
+around your application when using a remote control instead of a touchscreen.
+</p>
+<p>
+This lesson shows you how to optimize navigation for TV by:
+</p>
+
+<ul>
+  <li>Ensuring all layout controls are D-pad navigable.</li>
+  <li>Providing highly obvious feedback for UI navigation.</li>
+  <li>Placing layout controls for easy access.</li>
+</ul>
+
+<h2 id="HandleDpadNavigation">Handle D-pad Navigation</h2> 
+
+<p>
+On a TV, users navigate with controls on a TV remote, using either a D-pad or arrow keys. 
+This limits movement to up, down, left, and right. 
+To build a great TV-optimized app, you must provide a navigation scheme in which the user can 
+quickly learn how to navigate your app using the remote.
+</p>
+
+<p>
+When you design navigation for D-pad, follow these guidelines:
+</p>
+
+<ul>
+  <li>Ensure that the D-pad  can navigate to all the visible controls on the screen.</li>
+  <li>For scrolling lists with focus, D-pad up/down keys scroll the list and Enter key selects an item in the list. Ensure that users can 
+  select an element in the list and that the list still scrolls when an element is selected.</li> 
+  <li>Ensure that movement between controls is straightforward and predictable.</li>
+</ul>
+
+<p>
+Android usually handles navigation order between layout elements automatically, so you don't need to do anything extra. If the screen layout 
+makes navigation difficult, or if you want users to move through the layout in a specific way, you can set up explicit navigation for your 
+controls.  
+For example, for an {@code android.widget.EditText}, to define the next control to receive focus, use:
+<pre>
+&lt;EditText android:id="@+id/LastNameField" android:nextFocusDown="@+id/FirstNameField"\&gt;
+</pre>
+The following table lists all of the available navigation attributes:
+</p>
+
+<table>
+<tr>
+<th>Attribute</th>
+<th>Function</th>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusDown}</td>
+<td>Defines the next view to receive focus when the user navigates down.</td>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusLeft}</td>
+<td>Defines the next view to receive focus when the user navigates left.</td>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusRight}</td>
+<td>Defines the next view to receive focus when the user navigates right.</td>
+</tr>
+<tr>
+<td>{@link android.R.attr#nextFocusUp}</td>
+<td>Defines the next view to receive focus when the user navigates up.</td>
+</tr>
+</table>
+
+<p>
+To use one of these explicit navigation attributes, set the value to the ID (android:id value) of another widget in the layout. You should set 
+up the navigation order as a loop, so that the last control directs focus back to the first one.
+</p>
+
+<p>
+Note: You should only use these attributes to modify the navigation order if the default order that the system applies does not work well.
+</p>
+
+<h2 id="HandleFocusSelection">Provide Clear Visual Indication for Focus and Selection</h2>
+
+<p>
+Use appropriate color highlights for all navigable and selectable elements in the UI. This makes it easy for users to know whether the control 
+is currently focused or selected when they navigate with a D-pad. Also, use uniform highlight scheme across your application.
+</p>
+
+<p>
+Android provides <a href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">Drawable State List Resources</a> to implement highlights 
+for selected and focused controls. For example:
+</p>
+
+res/drawable/button.xml:
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?&gt;
+&lt;selector xmlns:android="http://schemas.android.com/apk/res/android"&gt;
+    &lt;item android:state_pressed="true"
+          android:drawable="@drawable/button_pressed" /&gt; &lt;!-- pressed --&gt;
+    &lt;item android:state_focused="true"
+          android:drawable="@drawable/button_focused" /&gt; &lt;!-- focused --&gt;
+    &lt;item android:state_hovered="true"
+          android:drawable="@drawable/button_focused" /&gt; &lt;!-- hovered --&gt;
+    &lt;item android:drawable="@drawable/button_normal" /&gt; &lt;!-- default --&gt;
+&lt;/selector&gt;
+</pre>
+
+<p>
+This layout XML applies the above state list drawable to a {@link android.widget.Button}:
+</p>
+<pre>
+&lt;Button
+    android:layout_height="wrap_content"
+    android:layout_width="wrap_content"
+    android:background="@drawable/button" /&gt;
+</pre>
+
+<p>
+Provide sufficient padding within the focusable and selectable controls so that the highlights around them are clearly visible. 
+</p>
+
+<h2 id="DesignForEasyNavigation">Design for Easy Navigation</h2>
+
+<p>
+Users should be able to navigate to any UI control with a couple of D-pad clicks. Navigation should be easy and  intuitive to 
+understand.  For any non-intuitive actions, provide users with written help, using a dialog triggered by a help button or action bar icon. 
+</p>
+
+<p>
+Predict the next screen that the user will want to navigate to and provide one click navigation to it. If the current screen UI is very sparse, 
+consider making it a multi pane screen. Use fragments for making multi-pane screens. For example, consider the multi-pane UI below with continent names 
+on the left and list of cool places in each continent on the right. 
+</p>
+
+<img src="{@docRoot}images/training/cool-places.png" alt="" />
+
+<p>
+The above UI consists of three Fragments - <code>left_side_action_controls</code>, <code>continents</code> and 
+<code>places</code> - as shown in its layout 
+xml file below. Such multi-pane UIs make D-pad navigation easier and make good use of the horizontal screen space for 
+TVs.
+</p>
+res/layout/cool_places.xml
+<pre>
+&lt;LinearLayout   
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="horizontal"
+   &gt;
+   &lt;fragment
+        android:id="@+id/left_side_action_controls"
+        android:layout_width="0px"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="10dip"
+        android:layout_weight="0.2"/&gt;
+    &lt;fragment
+        android:id="@+id/continents"
+        android:layout_width="0px"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="10dip"
+        android:layout_weight="0.2"/&gt;
+
+    &lt;fragment
+        android:id="@+id/places"
+        android:layout_width="0px"
+        android:layout_height="match_parent"
+        android:layout_marginLeft="10dip"
+        android:layout_weight="0.6"/&gt;
+
+&lt;/LinearLayout&gt;
+</pre>
+
+<p>
+Also, notice in the UI layout above action controls are on the left hand side of a vertically scrolling list to make 
+them easily accessible using D-pad. 
+In general, for layouts with horizontally scrolling components, place action controls on left or right hand side and 
+vice versa for vertically scrolling components.
+</p>
+
diff --git a/docs/html/training/tv/unsupported-features-tv.jd b/docs/html/training/tv/unsupported-features-tv.jd
new file mode 100644
index 0000000..a9f090b
--- /dev/null
+++ b/docs/html/training/tv/unsupported-features-tv.jd
@@ -0,0 +1,157 @@
+page.title=Handling Features Not Supported on TV
+parent.title=Designing for TV
+parent.link=index.html
+
+trainingnavtop=true
+previous.title=Optimizing Navigation for TV
+previous.link=optimizing-navigation-tv.html
+
+@jd:body
+
+<div id="tb-wrapper">
+<div id="tb">
+
+<h2>This lesson teaches you to</h2>
+<ol>
+  <li><a href="#WorkaroundUnsupportedFeatures">Work Around Features Not Supported on TV</a></li>
+  <li><a href="#CheckAvailableFeatures">Check for Available Features at Runtime</a></li>
+</ol>
+
+</div>
+</div>
+
+<p>
+TVs are much different from other Android-powered devices:
+</p>
+<ul>
+  <li>They're not mobile.</li>
+  <li>Out of habit, people use them for watching media with little or no interaction.</li>
+  <li>People interact with them from a distance.</li>
+</ul>
+
+<p>
+Because TVs have a different purpose from other devices, they usually don't have hardware features 
+that other Android-powered devices often have. For this reason, the Android system does not 
+support the following features for a TV device:
+<table>
+<tr>
+<th>Hardware</th>
+<th>Android feature descriptor</th>
+</tr>
+<tr>
+<td>Camera</td>
+<td>android.hardware.camera</td>
+</tr>
+<tr>
+<td>GPS</td>
+<td>android.hardware.location.gps</td>
+</tr>
+<tr>
+<td>Microphone</td>
+<td>android.hardware.microphone</td>
+</tr>
+<tr>
+<td>Near Field Communications (NFC)</td>
+<td>android.hardware.nfc</td>
+</tr>
+<tr>
+<td>Telephony</td>
+<td>android.hardware.telephony</td>
+</tr>
+<tr>
+<td>Touchscreen</td>
+<td>android.hardware.touchscreen</td>
+</tr>
+</table>
+</p>
+
+<p>
+This lesson shows you how to work around features that are not available on TV by:
+<ul>
+  <li>Providing work arounds for some non-supported features.</li>
+  <li>Checking for available features at runtime and conditionally activating/deactivating certain code 
+  paths based on availability of those features.</li>
+</ul>
+</p>
+
+
+<h2 id="WorkaroundUnsupportedFeatures">Work Around Features Not Supported on TV</h2> 
+
+<p>
+Android doesn't support touchscreen interaction for TV devices, most TVs don't have touch screens, 
+and interacting with a TV using a touchscreen is not consistent with the 10 foot environment. For 
+these reasons, users interact with Android-powered TVs using a remote. In consideration of this, 
+ensure that every control in your app can be accessed with the D-pad. Refer back to the previous two lessons 
+<a href="{@docRoot}training/tv/optimizing-layouts-tv.html">Optimizing Layouts for TV</a> and 
+<a href="{@docRoot}training/tv/optimizing-navigation-tv.html">Optimize Navigation for TV</a> for
+more details 
+on this topic. The Android system assumes that a device has a touchscreen, so if you want your application 
+to run on a TV, you must <strong>explicitly</strong> disable the touchscreen requirement in your manifest file:
+<pre>
+&lt;uses-feature android:name="android.hardware.touchscreen" android:required="false"/&gt;
+</pre>
+</p> 
+
+<p>
+Although a TV doesn't have a camera, you can still provide a photography-related application on a TV. 
+For example, if you have an app that takes, views and edits photos, you can disable its picture-taking 
+functionality for TVs and still allow users to view and even edit photos. The next section talks about how to 
+deactivate or activate specific functions in the application based on runtime device type detection.
+</p>
+
+<p>
+Because TVs are stationary, indoor devices, they don't have built-in GPS. If your application uses location 
+information, allow users to search for a location or use a "static" location provider to get 
+a location from the zip code configured during the TV setup.
+<pre>
+LocationManager locationManager = (LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
+Location location = locationManager.getLastKnownLocation("static");
+Geocoder geocoder = new Geocoder(this);
+Address address = null;
+
+try {
+  address = geocoder.getFromLocation(location.getLatitude(), location.getLongitude(), 1).get(0);
+  Log.d("Zip code", address.getPostalCode());
+
+} catch (IOException e) {
+  Log.e(TAG, "Geocoder error", e);
+}
+</pre>
+</p>
+
+<p>
+TVs usually don't support microphones, but if you have an application that uses voice control, 
+you can create a mobile device app that takes voice input and then acts as a remote control for a TV.
+</p>
+
+<h2 id="CheckAvailableFeatures">Check for Available Features at Runtime</h2>
+
+<p>
+To check if a feature is available at runtime, call 
+{@link android.content.pm.PackageManager#hasSystemFeature(String)}.
+ This method takes a single argument : a string corresponding to the 
+feature you want to check. For example, to check for touchscreen, use 
+{@link android.content.pm.PackageManager#hasSystemFeature(String)} with the argument 
+{@link android.content.pm.PackageManager#FEATURE_TOUCHSCREEN}.
+</p>
+
+<p>
+The following code snippet demonstrates how to detect device type at runtime based on supported features:
+
+<pre>
+// Check if android.hardware.telephony feature is available.
+if (getPackageManager().hasSystemFeature("android.hardware.telephony")) {
+   Log.d("Mobile Test", "Running on phone");
+// Check if android.hardware.touchscreen feature is available.
+} else if (getPackageManager().hasSystemFeature("android.hardware.touchscreen")) {
+   Log.d("Tablet Test", "Running on devices that don't support telphony but have a touchscreen.");
+} else {
+    Log.d("TV Test", "Running on a TV!");
+}
+</pre>
+</p>
+
+<p>
+This is just one example of using runtime checks to deactivate app functionality that depends on features 
+that aren't available on TVs.
+</p>
\ No newline at end of file
diff --git a/docs/html/tv/images/hero.jpg b/docs/html/tv/images/hero.jpg
index c42a436..e951167 100644
--- a/docs/html/tv/images/hero.jpg
+++ b/docs/html/tv/images/hero.jpg
Binary files differ
diff --git a/docs/html/tv/index.jd b/docs/html/tv/index.jd
index e1cae8c..5c48e49 100644
--- a/docs/html/tv/index.jd
+++ b/docs/html/tv/index.jd
@@ -4,7 +4,6 @@
 no_footer_links=true
 page.type=about
 
-
 @jd:body
 
 <style>
@@ -14,17 +13,9 @@
 }
 </style>
 
-<style>
-#footer {
-    display: none;
-}
-.content-footer {
-  display: none;
-}
-</style>
-
 
 <div class="landing-body-content">
+
   <div class="landing-hero-container">
 
     <div class="landing-section tv-hero">
@@ -42,9 +33,11 @@
                     Put your app on TV and bring everyone into
                     the action.</p>
                 </div>
+              </div>
 
               <div class="landing-body">
-                <a href="{@docRoot}preview/tv/index.html" class="landing-button landing-primary" style="margin-top: 40px;">
+                <a href="{@docRoot}preview/tv/start/index.html" class="landing-button
+                  landing-primary" style="margin-top: 40px;">
                   Get Started
                 </a>
               </div>
@@ -58,11 +51,10 @@
         </a>
       </div>
     </div> <!-- end .landing-section .landing-hero -->
-  </div> <!-- end .landing-hero-container -->
 
     <div class="landing-rest-of-page">
 
-      <div class="landing-section landing-gray-background" id="reimagine-your-app">
+      <div class="landing-section" style="background-color:#f5f5f5" id="reimagine-your-app">
         <div class="wrap">
           <div class="landing-section-header">
             <div class="landing-h1">Reimagine Your App</div>
@@ -71,7 +63,6 @@
             </div>
           </div>
 
-
           <div class="landing-body">
             <div class="landing-breakout cols">
 
@@ -119,13 +110,13 @@
         </div>  <!-- end .wrap -->
       </div>  <!-- end .landing-section -->
 
-      <div class="landing-section" style="background-color:#f5f5f5">
+      <div class="landing-section landing-gray-background">
         <div class="wrap">
           <div class="landing-section-header">
             <div class="landing-h1">Build to Entertain</div>
             <div class="landing-subhead">
-              Android TV let's you engage your users in a new, shared environment.<br>
-              Find out how to get your app ready for it's big screen debut.
+              Android TV lets you engage your users in a new, shared environment.<br>
+              Find out how to get your app ready for its big-screen debut.
             </div>
           </div>
 
@@ -142,7 +133,7 @@
                   catalogs.
                 </p>
                 <p class="landing-small">
-                  <a href="{@docRoot}design/tv/index.html">Learn pre-built fragments</a>
+                  <a href="{@docRoot}preview/tv/ui/browse.html">Learn pre-built fragments</a>
                 </p>
               </div>
 
@@ -151,11 +142,10 @@
 
                 <p>Get Found</p>
                 <p class="landing-small">
-                  Give your content the attention it deserves by including it in Android TV's global
-                  search results.
+                  Help users find your content quickly with in-app searching.
                 </p>
                 <p class="landing-small">
-                  <a href="{@docRoot}design/tv/index.html">Learn about TV design</a>
+                  <a href="{@docRoot}preview/tv/ui/in-app-search.html">Learn about app search</a>
                 </p>
               </div>
 
@@ -167,7 +157,8 @@
                   Suggest content from your app to keep your users coming back.
                 </p>
                 <p class="landing-small">
-                  <a href="{@docRoot}design/tv/index.html">Learn about design for TV</a>
+                  <a href="{@docRoot}preview/tv/ui/recommendations.html">Learn about
+                    recommendations</a>
                 </p>
               </div>
 
@@ -182,28 +173,26 @@
           <div class="landing-section-header">
             <div class="landing-h1 landing-align-left">Get Started with Android TV</div>
             <div class="landing-body">
-              <p>You can begin building apps right away using these developer resources.</p>
+              <p>Begin building TV apps right away using these developer resources:</p>
             </div>
           </div>
 
           <div class="landing-body">
             <div class="landing-breakout cols">
-              <div class="col-8">
-                <p>Preview SDK</p>
+              <div class="col-8" style="margin-left: -8px;">
+                <p style="font-size: 24px;">L-Preview SDK</p>
                 <p>
-                  Get started building for Android TV using the Android L-preview SDK. The preview
-                  SDK includes the Android TV emulator so you can start building your TV app right
-                  away.
+                  The preview SDK includes all the tools you need to build and test apps for TV.
+                  Download it and start creating your big-screen app.
                 </p>
 
               </div>
 
               <div class="col-8">
-                <p>ADT-1 Developer Kit</p>
+                <p style="font-size: 24px;">ADT-1 Developer Kit</p>
                 <p>
-                  While supplies last, developers can request an ADT-1 Developer Kit, a compact and
-                  powerful streaming media player and gamepad, ideal for developing apps for Android
-                  TV.
+                  Request an ADT-1 Developer Kit, a compact and powerful streaming media player
+                  and gamepad, ideal for developing and testing apps for TV.
                 </p>
 
               </div>
@@ -215,15 +204,16 @@
             <div class="landing-breakout cols">
 
               <div class="col-8">
-                <a href="{@docRoot}preview/download.html" class="landing-button landing-secondary">
+                <a href="{@docRoot}preview/setup-sdk.html" class="landing-button landing-primary">
                   Download the Preview SDK
                 </a>
               </div>
 
               <div class="col-8">
-                <a href="{@docRoot}tv/adt-1/request.html" class="landing-button landing-secondary">
+                <a href="{@docRoot}preview/tv/adt-1/request.html" class="landing-button landing-primary">
                   Request ADT-1 Developer Kit
                 </a>
+              </div>
             </div>
           </div>
 
@@ -232,31 +222,33 @@
 
     </div> <!-- end .landing-rest-of-page -->
 
-
-    <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
-      <div class="layout-content-col col-16" style="padding-top:4px">
-        <style>#___plusone_0 {float:right !important;}</style>
-        <div class="g-plusone" data-size="medium"></div>
+      <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement"
+        style="border-top: none;">
+        <div class="layout-content-col col-16" style="padding-top:4px">
+          <style>#___plusone_0 {float:right !important;}</style>
+          <div class="g-plusone" data-size="medium"></div>
+        </div>
       </div>
-    </div>
-    <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
-      <div id="copyright">
-        Except as noted, this content is
-        licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
-        Creative Commons Attribution 2.5</a>. For details and
-        restrictions, see the <a href="/license.html">Content
-        License</a>.
+      <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
+        <div id="copyright">
+          Except as noted, this content is
+          licensed under <a href="http://creativecommons.org/licenses/by/2.5/">
+          Creative Commons Attribution 2.5</a>. For details and
+          restrictions, see the <a href="/license.html">Content
+          License</a>.
+        </div>
       </div>
-    </div>
 
-
-  </div> <!-- end landing-body-content -->
+  </div> <!-- end .landing-hero-container -->
 
   <script>
   $("a.landing-down-arrow").on("click", function(e) {
     $("body").animate({
-      scrollTop: $(".wear-hero").height() + 76
+      scrollTop: $(".tv-hero").height() + 120
     }, 1000, "easeOutQuint");
     e.preventDefault();
   });
   </script>
+
+</div> <!-- end landing-body-content -->
+
diff --git a/docs/html/wear/images/hero.jpg b/docs/html/wear/images/hero.jpg
index 7850a81..0b4debb 100644
--- a/docs/html/wear/images/hero.jpg
+++ b/docs/html/wear/images/hero.jpg
Binary files differ
diff --git a/docs/html/wear/index.jd b/docs/html/wear/index.jd
index 59d5506..bc08aa0 100644
--- a/docs/html/wear/index.jd
+++ b/docs/html/wear/index.jd
@@ -66,9 +66,9 @@
         </a>
       </div>
     </div> <!-- end .landing-section .landing-hero -->
-  </div> <!-- end .landing-hero-container -->
 
-    <div class="landing-rest-of-page">
+
+    <div class="landing-rest-of-page" style="margin-top:80px">
       <div class="landing-section" id="extending-android-to-wearables">
         <div class="wrap">
           <div class="landing-section-header">
@@ -84,9 +84,8 @@
             <div class="landing-breakout cols">
               <div class="col-3-wide">
 
-                <div class="landing-inset-video-container">
-                  <img class="landing-bezel-only" src="{@docRoot}wear/images/screens/bezel.png" alt="">
-                  <img class="gif" src="{@docRoot}wear/images/screens/reservation_animated.gif">
+                <div class="framed-wear-square">
+                  <img src="{@docRoot}wear/images/screens/reservation_animated.gif">
                 </div>
 
                 <p class="landing-small">
@@ -183,6 +182,56 @@
         </div> <!-- end .wrap -->
       </div> <!-- end .landing-section -->
 
+      <div class="landing-section landing-white-background">
+        <div class="wrap">
+          <div class="landing-section-header">
+            <div class="landing-h2">Building an Ecosystem</div>
+            <div class="landing-body landing-align-center">
+              <p class="landing-small">
+                We’re working with partners around the world to build watches powered by Android Wear!
+              </p>
+            </div>
+          </div>
+
+          <div class="landing-partners cols">
+            <div class="col-4">
+              <img src="/wear/images/partners/asus.png" alt="Asus">
+            </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/broadcom.png" alt="Broadcom">
+            </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/fossil.png" alt="Fossil">
+            </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/htc.png" alt="HTC">
+            </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/intel.png" alt="Intel">
+            </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/lg.png" alt="LG">
+            </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/mediatek.png" alt="Mediatek">
+            </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/mips.png" alt="MIPS">
+            </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/motorola.png" alt="Motorola">
+            </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/qualcomm.png" alt="Qualcomm">
+            </div>
+            <div class="col-4">
+              <img src="/wear/images/partners/samsung.png" alt="Samsung">
+            </div>
+          </div>
+        </div> <!-- end .wrap -->
+      </div>
+
+
       <div class="landing-section landing-red-background">
         <div class="wrap">
           <div class="landing-section-header">
@@ -196,7 +245,7 @@
             </div>
           </div>
           <div class="landing-body">
-            <a href="{@docRoot}training/building-wearables.html" class="landing-button landing-secondary" style="margin-top: 20px;">
+            <a href="{@docRoot}training/building-wearables.html" class="landing-button landing-primary" style="margin-top: 20px;">
               Get Started
             </a>
           </div>
@@ -236,7 +285,7 @@
                   </p>
                   <br>
                     <p class="landing-small">
-                    <a target="_blank" href="http://android-developers.blogspot.com/2014/03/android-wear-developer-preview.html">Android Developers Blog</a>
+                    <a target="_blank" href="http://android-developers.blogspot.com">Android Developers Blog</a>
                     </p>
                   <p></p>
                 </div>
@@ -264,7 +313,7 @@
     <div class="content-footer wrap" itemscope="" itemtype="http://schema.org/SiteNavigationElement">
       <div class="layout-content-col col-16" style="padding-top:4px">
         <style>#___plusone_0 {float:right !important;}</style>
-        <div id="___plusone_0" style="text-indent: 0px; margin: 0px; padding: 0px; border-style: none; float: none; line-height: normal; font-size: 1px; vertical-align: baseline; display: inline-block; width: 90px; height: 20px; background: transparent;"><iframe frameborder="0" hspace="0" marginheight="0" marginwidth="0" scrolling="no" style="position: static; top: 0px; width: 90px; margin: 0px; border-style: none; left: 0px; visibility: visible; height: 20px;" tabindex="0" vspace="0" width="100%" id="I0_1402525433965" name="I0_1402525433965" src="https://apis.google.com/u/0/_/+1/fastbutton?usegapi=1&amp;size=medium&amp;origin=http%3A%2F%2Frobertly.mtv%3A8080&amp;url=http%3A%2F%2Frobertly.mtv%3A8080%2Fwear%2Findex.html&amp;gsrc=3p&amp;jsh=m%3B%2F_%2Fscs%2Fapps-static%2F_%2Fjs%2Fk%3Doz.gapi.en.QxHQHBkhz7M.O%2Fm%3D__features__%2Fam%3DUQ%2Frt%3Dj%2Fd%3D1%2Fz%3Dzcms%2Frs%3DAItRSTMLrMyRVKsu2FQoRingre3w1MT49A#_methods=onPlusOne%2C_ready%2C_close%2C_open%2C_resizeMe%2C_renderstart%2Concircled%2Cdrefresh%2Cerefresh%2Conload&amp;id=I0_1402525433965&amp;parent=http%3A%2F%2Frobertly.mtv%3A8080&amp;pfname=&amp;rpctoken=32453860" data-gapiattached="true" title="+1"></iframe></div>
+        <div class="g-plusone" data-size="medium"></div>
       </div>
     </div>
     <div id="footer" class="wrap" style="width:940px;position:relative;top:-35px;z-index:-1">
@@ -276,5 +325,16 @@
         License</a>.
       </div>
     </div>
-  </div> <!-- end landing-body-content -->
+  </div> <!-- end .landing-hero-container -->
+
+  <script>
+  $("a.landing-down-arrow").on("click", function(e) {
+    $("body").animate({
+      scrollTop: $(".wear-hero").height() + 120
+    }, 1000, "easeOutQuint");
+    e.preventDefault();
+  });
+  </script>
+
+</div> <!-- end landing-body-content -->
 
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 158801c..99596ef 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -1712,7 +1712,8 @@
      * the pos array.
      *
      * This method does not support glyph composition and decomposition and
-     * should therefore not be used to render complex scripts.
+     * should therefore not be used to render complex scripts. It also doesn't
+     * handle supplementary characters (eg emoji).
      *
      * @param text     The text to be drawn
      * @param index    The index of the first character to draw
@@ -1727,8 +1728,9 @@
         if (index < 0 || index + count > text.length || count*2 > pos.length) {
             throw new IndexOutOfBoundsException();
         }
-        native_drawPosText(mNativeCanvasWrapper, text, index, count, pos,
-                paint.mNativePaint);
+        for (int i = 0; i < count; i++) {
+            drawText(text, index + i, 1, pos[i * 2], pos[i * 2 + 1], paint);
+        }
     }
 
     /**
@@ -1736,7 +1738,8 @@
      * the pos array.
      *
      * This method does not support glyph composition and decomposition and
-     * should therefore not be used to render complex scripts.
+     * should therefore not be used to render complex scripts. It also doesn't
+     * handle supplementary characters (eg emoji).
      *
      * @param text  The text to be drawn
      * @param pos   Array of [x,y] positions, used to position each character
@@ -1744,10 +1747,7 @@
      */
     @Deprecated
     public void drawPosText(@NonNull String text, @NonNull float[] pos, @NonNull Paint paint) {
-        if (text.length()*2 > pos.length) {
-            throw new ArrayIndexOutOfBoundsException();
-        }
-        native_drawPosText(mNativeCanvasWrapper, text, pos, paint.mNativePaint);
+        drawPosText(text.toCharArray(), 0, text.length(), pos, paint);
     }
 
     /**
@@ -1770,7 +1770,7 @@
         }
         native_drawTextOnPath(mNativeCanvasWrapper, text, index, count,
                 path.ni(), hOffset, vOffset,
-                paint.mBidiFlags, paint.mNativePaint);
+                paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
     }
 
     /**
@@ -1790,7 +1790,7 @@
             float vOffset, @NonNull Paint paint) {
         if (text.length() > 0) {
             native_drawTextOnPath(mNativeCanvasWrapper, text, path.ni(), hOffset, vOffset,
-                    paint.mBidiFlags, paint.mNativePaint);
+                    paint.mBidiFlags, paint.mNativePaint, paint.mNativeTypeface);
         }
     }
 
@@ -2009,23 +2009,16 @@
             int start, int count, int contextStart, int contextCount,
             float x, float y, boolean isRtl, long nativePaint, long nativeTypeface);
 
-    private static native void native_drawPosText(long nativeCanvas,
-                                                  char[] text, int index,
-                                                  int count, float[] pos,
-                                                  long nativePaint);
-    private static native void native_drawPosText(long nativeCanvas,
-                                                  String text, float[] pos,
-                                                  long nativePaint);
     private static native void native_drawTextOnPath(long nativeCanvas,
                                                      char[] text, int index,
                                                      int count, long nativePath,
                                                      float hOffset,
                                                      float vOffset, int bidiFlags,
-                                                     long nativePaint);
+                                                     long nativePaint, long nativeTypeface);
     private static native void native_drawTextOnPath(long nativeCanvas,
                                                      String text, long nativePath,
                                                      float hOffset,
                                                      float vOffset,
-                                                     int flags, long nativePaint);
+                                                     int flags, long nativePaint, long nativeTypeface);
     private static native void finalizer(long nativeCanvas);
 }
diff --git a/graphics/java/android/graphics/Picture.java b/graphics/java/android/graphics/Picture.java
index a021165..5aa7c6a 100644
--- a/graphics/java/android/graphics/Picture.java
+++ b/graphics/java/android/graphics/Picture.java
@@ -31,18 +31,13 @@
     private Canvas mRecordingCanvas;
     private final long mNativePicture;
 
-    /**
-     * @hide
-     */
-    public final boolean createdFromStream;
-
     private static final int WORKING_STREAM_STORAGE = 16 * 1024;
 
     /**
      * Creates an empty picture that is ready to record.
      */
     public Picture() {
-        this(nativeConstructor(0), false);
+        this(nativeConstructor(0));
     }
 
     /**
@@ -51,7 +46,23 @@
      * changes will not be reflected in this picture.
      */
     public Picture(Picture src) {
-        this(nativeConstructor(src != null ? src.mNativePicture : 0), false);
+        this(nativeConstructor(src != null ? src.mNativePicture : 0));
+    }
+
+    private Picture(long nativePicture) {
+        if (nativePicture == 0) {
+            throw new RuntimeException();
+        }
+        mNativePicture = nativePicture;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        try {
+            nativeDestructor(mNativePicture);
+        } finally {
+            super.finalize();
+        }
     }
 
     /**
@@ -85,13 +96,17 @@
      * Get the width of the picture as passed to beginRecording. This
      * does not reflect (per se) the content of the picture.
      */
-    public native int getWidth();
+    public int getWidth() {
+      return nativeGetWidth(mNativePicture);
+    }
 
     /**
      * Get the height of the picture as passed to beginRecording. This
      * does not reflect (per se) the content of the picture.
      */
-    public native int getHeight();
+    public int getHeight() {
+      return nativeGetHeight(mNativePicture);
+    }
 
     /**
      * Draw this picture on the canvas.
@@ -130,7 +145,7 @@
      */
     @Deprecated
     public static Picture createFromStream(InputStream stream) {
-        return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]), true);
+        return new Picture(nativeCreateFromStream(stream, new byte[WORKING_STREAM_STORAGE]));
     }
 
     /**
@@ -159,32 +174,12 @@
         }
     }
 
-    protected void finalize() throws Throwable {
-        try {
-            nativeDestructor(mNativePicture);
-        } finally {
-            super.finalize();
-        }
-    }
-
-    final long ni() {
-        return mNativePicture;
-    }
-
-    private Picture(long nativePicture, boolean fromStream) {
-        if (nativePicture == 0) {
-            throw new RuntimeException();
-        }
-        mNativePicture = nativePicture;
-        createdFromStream = fromStream;
-    }
-
     // return empty picture if src is 0, or a copy of the native src
     private static native long nativeConstructor(long nativeSrcOr0);
-    private static native long nativeCreateFromStream(InputStream stream,
-                                                byte[] storage);
-    private static native long nativeBeginRecording(long nativeCanvas,
-                                                    int w, int h);
+    private static native long nativeCreateFromStream(InputStream stream, byte[] storage);
+    private static native int nativeGetWidth(long nativePicture);
+    private static native int nativeGetHeight(long nativePicture);
+    private static native long nativeBeginRecording(long nativeCanvas, int w, int h);
     private static native void nativeEndRecording(long nativeCanvas);
     private static native void nativeDraw(long nativeCanvas, long nativePicture);
     private static native boolean nativeWriteToStream(long nativePicture,
@@ -201,18 +196,15 @@
 
         @Override
         public void setBitmap(Bitmap bitmap) {
-            throw new RuntimeException(
-                                "Cannot call setBitmap on a picture canvas");
+            throw new RuntimeException("Cannot call setBitmap on a picture canvas");
         }
 
         @Override
         public void drawPicture(Picture picture) {
             if (mPicture == picture) {
-                throw new RuntimeException(
-                            "Cannot draw a picture into its recording canvas");
+                throw new RuntimeException("Cannot draw a picture into its recording canvas");
             }
             super.drawPicture(picture);
         }
     }
 }
-
diff --git a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
index 968c0ec..c787fb0 100644
--- a/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/AnimatedVectorDrawable.java
@@ -34,10 +34,91 @@
 import java.util.ArrayList;
 
 /**
- * AnimatedVectorDrawable can use ObjectAnimator and AnimatorSet to animate
- * the property of the VectorDrawable.
+ * This class uses {@link android.animation.ObjectAnimator} and
+ * {@link android.animation.AnimatorSet} to animate the properties of a
+ * {@link android.graphics.drawable.VectorDrawable} to create an animated drawable.
+ * <p>
+ * AnimatedVectorDrawable are normally defined as 3 separate XML files.
+ * </p>
+ * <p>
+ * First is the XML file for {@link android.graphics.drawable.VectorDrawable}.
+ * Note that we allow the animation happen on the group's attributes and path's
+ * attributes, which requires they are uniquely named in this xml file. Groups
+ * and paths without animations do not need names.
+ * </p>
+ * <li>Here is a simple VectorDrawable in this vectordrawable.xml file.
+ * <pre>
+ * &lt;vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot; &gt;
+ *     &lt;size
+ *         android:height=&quot;64dp&quot;
+ *         android:width=&quot;64dp&quot; /&gt;
+ *     &lt;viewport
+ *         android:viewportHeight=&quot;600&quot;
+ *         android:viewportWidth=&quot;600&quot; /&gt;
+ *     &lt;group
+ *         android:name=&quot;rotationGroup&quot;
+ *         android:pivotX=&quot;300.0&quot;
+ *         android:pivotY=&quot;300.0&quot;
+ *         android:rotation=&quot;45.0&quot; &gt;
+ *         &lt;path
+ *             android:name=&quot;v&quot;
+ *             android:fill=&quot;#000000&quot;
+ *             android:pathData=&quot;M300,70 l 0,-70 70,70 0,0 -70,70z&quot; /&gt;
+ *     &lt;/group&gt;
+ * &lt;/vector&gt;
+ * </pre></li>
+ * <p>
+ * Second is the AnimatedVectorDrawable's xml file, which defines the target
+ * VectorDrawable, the target paths and groups to animate, the properties of the
+ * path and group to animate and the animations defined as the ObjectAnimators
+ * or AnimatorSets.
+ * </p>
+ * <li>Here is a simple AnimatedVectorDrawable defined in this avd.xml file.
+ * Note how we use the names to refer to the groups and paths in the vectordrawable.xml.
+ * <pre>
+ * &lt;animated-vector xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
+ *   android:drawable=&quot;@drawable/vectordrawable&quot; &gt;
+ *     &lt;target
+ *         android:name=&quot;rotationGroup&quot;
+ *         android:animation=&quot;@anim/rotation&quot; /&gt;
+ *     &lt;target
+ *         android:name=&quot;v&quot;
+ *         android:animation=&quot;@anim/path_morph&quot; /&gt;
+ * &lt;/animated-vector&gt;
+ * </pre></li>
+ * <p>
+ * Last is the Animator xml file, which is the same as a normal ObjectAnimator
+ * or AnimatorSet.
+ * To complete this example, here are the 2 animator files used in avd.xml:
+ * rotation.xml and path_morph.xml.
+ * </p>
+ * <li>Here is the rotation.xml, which will rotate the target group for 360 degrees.
+ * <pre>
+ * &lt;objectAnimator
+ *     android:duration=&quot;6000&quot;
+ *     android:propertyName=&quot;rotation&quot;
+ *     android:valueFrom=&quot;0&quot;
+ *     android:valueTo=&quot;360&quot; /&gt;
+ * </pre></li>
+ * <li>Here is the path_morph.xml, which will morph the path from one shape to
+ * the other. Note that the paths must be compatible for morphing.
+ * In more details, the paths should have exact same length of commands , and
+ * exact same length of parameters for each commands.
+ * Note that the path string are better stored in strings.xml for reusing.
+ * <pre>
+ * &lt;set xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
+ *     &lt;objectAnimator
+ *         android:duration=&quot;3000&quot;
+ *         android:propertyName=&quot;pathData&quot;
+ *         android:valueFrom=&quot;M300,70 l 0,-70 70,70 0,0   -70,70z&quot;
+ *         android:valueTo=&quot;M300,70 l 0,-70 70,0  0,140 -70,0 z&quot;
+ *         android:valueType=&quot;pathType&quot;/&gt;
+ * &lt;/set&gt;
+ * </pre></li>
  *
- * @hide
+ * @attr ref android.R.styleable#AnimatedVectorDrawable_drawable
+ * @attr ref android.R.styleable#AnimatedVectorDrawableTarget_name
+ * @attr ref android.R.styleable#AnimatedVectorDrawableTarget_animation
  */
 public class AnimatedVectorDrawable extends Drawable implements Animatable {
     private static final String LOGTAG = AnimatedVectorDrawable.class.getSimpleName();
diff --git a/graphics/java/android/graphics/drawable/BitmapDrawable.java b/graphics/java/android/graphics/drawable/BitmapDrawable.java
index be940df..e080375 100644
--- a/graphics/java/android/graphics/drawable/BitmapDrawable.java
+++ b/graphics/java/android/graphics/drawable/BitmapDrawable.java
@@ -757,7 +757,18 @@
 
         final int tileMode = a.getInt(R.styleable.BitmapDrawable_tileMode, TILE_MODE_UNDEFINED);
         if (tileMode != TILE_MODE_UNDEFINED) {
-            setTileModeInternal(tileMode);
+            final Shader.TileMode mode = parseTileMode(tileMode);
+            setTileModeXY(mode, mode);
+        }
+
+        final int tileModeX = a.getInt(R.styleable.BitmapDrawable_tileModeX, TILE_MODE_UNDEFINED);
+        if (tileModeX != TILE_MODE_UNDEFINED) {
+            setTileModeX(parseTileMode(tileModeX));
+        }
+
+        final int tileModeY = a.getInt(R.styleable.BitmapDrawable_tileModeY, TILE_MODE_UNDEFINED);
+        if (tileModeY != TILE_MODE_UNDEFINED) {
+            setTileModeY(parseTileMode(tileModeY));
         }
 
         // Update local properties.
@@ -783,20 +794,16 @@
         }
     }
 
-    private void setTileModeInternal(final int tileMode) {
+    private static Shader.TileMode parseTileMode(int tileMode) {
         switch (tileMode) {
-            case TILE_MODE_DISABLED:
-                setTileModeXY(null, null);
-                break;
             case TILE_MODE_CLAMP:
-                setTileModeXY(Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
-                break;
+                return Shader.TileMode.CLAMP;
             case TILE_MODE_REPEAT:
-                setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
-                break;
+                return Shader.TileMode.REPEAT;
             case TILE_MODE_MIRROR:
-                setTileModeXY(Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);
-                break;
+                return Shader.TileMode.MIRROR;
+            default:
+                return null;
         }
     }
 
@@ -919,7 +926,9 @@
     }
 
     /**
-     * Initializes local dynamic properties from state.
+     * Initializes local dynamic properties from state. This should be called
+     * after significant state changes, e.g. from the One True Constructor and
+     * after inflating or applying a theme.
      */
     private void initializeWithState(BitmapState state, Resources res) {
         if (res != null) {
@@ -928,7 +937,7 @@
             mTargetDensity = state.mTargetDensity;
         }
 
-        updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
         computeBitmapSize();
     }
 }
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index cb88e3d..40b55a7 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -1260,8 +1260,10 @@
     /**
      * Parses a {@link android.graphics.PorterDuff.Mode} from a tintMode
      * attribute's enum value.
+     *
+     * @hide
      */
-    static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {
+    public static PorterDuff.Mode parseTintMode(int value, Mode defaultMode) {
         switch (value) {
             case 3: return Mode.SRC_OVER;
             case 5: return Mode.SRC_IN;
diff --git a/graphics/java/android/graphics/drawable/DrawableContainer.java b/graphics/java/android/graphics/drawable/DrawableContainer.java
index 8be6eb7..38b8aaf 100644
--- a/graphics/java/android/graphics/drawable/DrawableContainer.java
+++ b/graphics/java/android/graphics/drawable/DrawableContainer.java
@@ -65,8 +65,6 @@
     private long mExitAnimationEnd;
     private Drawable mLastDrawable;
 
-    private Insets mInsets = Insets.NONE;
-
     // overrides from Drawable
 
     @Override
@@ -118,7 +116,10 @@
      */
     @Override
     public Insets getOpticalInsets() {
-        return mInsets;
+        if (mCurrDrawable != null) {
+            return mCurrDrawable.getOpticalInsets();
+        }
+        return Insets.NONE;
     }
 
     @Override
@@ -203,9 +204,6 @@
         }
         if (mCurrDrawable != null) {
             mCurrDrawable.setBounds(bounds);
-
-            // Must obtain optical insets after setting bounds.
-            mInsets = mCurrDrawable.getOpticalInsets();
         }
     }
 
@@ -422,15 +420,9 @@
                 d.setBounds(getBounds());
                 d.setLayoutDirection(getLayoutDirection());
                 d.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
-
-                // Must obtain optical insets after setting bounds.
-                mInsets = d.getOpticalInsets();
-            } else {
-                mInsets = Insets.NONE;
             }
         } else {
             mCurrDrawable = null;
-            mInsets = Insets.NONE;
             mCurIndex = -1;
         }
 
diff --git a/graphics/java/android/graphics/drawable/GradientDrawable.java b/graphics/java/android/graphics/drawable/GradientDrawable.java
index 005b8ef..0ccce93 100644
--- a/graphics/java/android/graphics/drawable/GradientDrawable.java
+++ b/graphics/java/android/graphics/drawable/GradientDrawable.java
@@ -135,18 +135,17 @@
     private Paint mStrokePaint;   // optional, set by the caller
     private ColorFilter mColorFilter;   // optional, set by the caller
     private int mAlpha = 0xFF;  // modified by the caller
-    private boolean mDither;
 
     private final Path mPath = new Path();
     private final RectF mRect = new RectF();
 
     private Paint mLayerPaint;    // internal, used if we use saveLayer()
-    private boolean mRectIsDirty;   // internal state
+    private boolean mGradientIsDirty;   // internal state
     private boolean mMutated;
     private Path mRingPath;
     private boolean mPathIsDirty = true;
 
-    /** Current gradient radius, valid when {@link #mRectIsDirty} is false. */
+    /** Current gradient radius, valid when {@link #mGradientIsDirty} is false. */
     private float mGradientRadius;
 
     /**
@@ -384,7 +383,7 @@
      */
     public void setGradientType(int gradient) {
         mGradientState.setGradientType(gradient);
-        mRectIsDirty = true;
+        mGradientIsDirty = true;
         invalidateSelf();
     }
 
@@ -403,7 +402,7 @@
      */
     public void setGradientCenter(float x, float y) {
         mGradientState.setGradientCenter(x, y);
-        mRectIsDirty = true;
+        mGradientIsDirty = true;
         invalidateSelf();
     }
 
@@ -421,7 +420,7 @@
      */
     public void setGradientRadius(float gradientRadius) {
         mGradientState.setGradientRadius(gradientRadius, TypedValue.COMPLEX_UNIT_PX);
-        mRectIsDirty = true;
+        mGradientIsDirty = true;
         invalidateSelf();
     }
 
@@ -455,7 +454,7 @@
      */
     public void setUseLevel(boolean useLevel) {
         mGradientState.mUseLevel = useLevel;
-        mRectIsDirty = true;
+        mGradientIsDirty = true;
         invalidateSelf();
     }
 
@@ -483,7 +482,7 @@
      */
     public void setOrientation(Orientation orientation) {
         mGradientState.mOrientation = orientation;
-        mRectIsDirty = true;
+        mGradientIsDirty = true;
         invalidateSelf();
     }
 
@@ -501,7 +500,7 @@
      */
     public void setColors(int[] colors) {
         mGradientState.setColors(colors);
-        mRectIsDirty = true;
+        mGradientIsDirty = true;
         invalidateSelf();
     }
 
@@ -543,7 +542,7 @@
             if (mLayerPaint == null) {
                 mLayerPaint = new Paint();
             }
-            mLayerPaint.setDither(mDither);
+            mLayerPaint.setDither(st.mDither);
             mLayerPaint.setAlpha(mAlpha);
             mLayerPaint.setColorFilter(mColorFilter);
 
@@ -561,14 +560,14 @@
                 individual paints
             */
             mFillPaint.setAlpha(currFillAlpha);
-            mFillPaint.setDither(mDither);
+            mFillPaint.setDither(st.mDither);
             mFillPaint.setColorFilter(mColorFilter);
-            if (mColorFilter != null && mGradientState.mColorStateList == null) {
+            if (mColorFilter != null && st.mColorStateList == null) {
                 mFillPaint.setColor(mAlpha << 24);
             }
             if (haveStroke) {
                 mStrokePaint.setAlpha(currStrokeAlpha);
-                mStrokePaint.setDither(mDither);
+                mStrokePaint.setDither(st.mDither);
                 mStrokePaint.setColorFilter(mColorFilter);
             }
         }
@@ -638,10 +637,11 @@
 
     private void buildPathIfDirty() {
         final GradientState st = mGradientState;
-        if (mPathIsDirty || mRectIsDirty) {
+        if (mPathIsDirty) {
+            ensureValidRect();
             mPath.reset();
             mPath.addRoundRect(mRect, st.mRadiusArray, Path.Direction.CW);
-            mPathIsDirty = mRectIsDirty = false;
+            mPathIsDirty = false;
         }
     }
 
@@ -804,8 +804,8 @@
 
     @Override
     public void setDither(boolean dither) {
-        if (dither != mDither) {
-            mDither = dither;
+        if (dither != mGradientState.mDither) {
+            mGradientState.mDither = dither;
             invalidateSelf();
         }
     }
@@ -829,27 +829,27 @@
         super.onBoundsChange(r);
         mRingPath = null;
         mPathIsDirty = true;
-        mRectIsDirty = true;
+        mGradientIsDirty = true;
     }
 
     @Override
     protected boolean onLevelChange(int level) {
         super.onLevelChange(level);
-        mRectIsDirty = true;
+        mGradientIsDirty = true;
         mPathIsDirty = true;
         invalidateSelf();
         return true;
     }
 
     /**
-     * This checks mRectIsDirty, and if it is true, recomputes both our drawing
+     * This checks mGradientIsDirty, and if it is true, recomputes both our drawing
      * rectangle (mRect) and the gradient itself, since it depends on our
      * rectangle too.
      * @return true if the resulting rectangle is not empty, false otherwise
      */
     private boolean ensureValidRect() {
-        if (mRectIsDirty) {
-            mRectIsDirty = false;
+        if (mGradientIsDirty) {
+            mGradientIsDirty = false;
 
             Rect bounds = getBounds();
             float inset = 0;
@@ -1015,7 +1015,7 @@
         state.mThemeAttrs = a.extractThemeAttrs();
 
         state.mShape = a.getInt(R.styleable.GradientDrawable_shape, state.mShape);
-        mDither = a.getBoolean(R.styleable.GradientDrawable_dither, mDither);
+        state.mDither = a.getBoolean(R.styleable.GradientDrawable_dither, state.mDither);
 
         if (state.mShape == RING) {
             state.mInnerRadius = a.getDimensionPixelSize(
@@ -1459,6 +1459,8 @@
         public float mThicknessRatio = DEFAULT_THICKNESS_RATIO;
         public int mInnerRadius = -1;
         public int mThickness = -1;
+        public boolean mDither = false;
+
         private float mCenterX = 0.5f;
         private float mCenterY = 0.5f;
         private float mGradientRadius = 0.5f;
@@ -1510,6 +1512,7 @@
             mThicknessRatio = state.mThicknessRatio;
             mInnerRadius = state.mInnerRadius;
             mThickness = state.mThickness;
+            mDither = state.mDither;
             mCenterX = state.mCenterX;
             mCenterY = state.mCenterY;
             mGradientRadius = state.mGradientRadius;
@@ -1672,7 +1675,7 @@
 
         initializeWithState(state);
 
-        mRectIsDirty = true;
+        mGradientIsDirty = true;
         mMutated = false;
     }
 
diff --git a/graphics/java/android/graphics/drawable/Ripple.java b/graphics/java/android/graphics/drawable/Ripple.java
index 345400e..2d49365 100644
--- a/graphics/java/android/graphics/drawable/Ripple.java
+++ b/graphics/java/android/graphics/drawable/Ripple.java
@@ -142,14 +142,16 @@
     }
 
     private void clampStartingPosition() {
-        final float dX = mStartingX - mBounds.exactCenterX();
-        final float dY = mStartingY - mBounds.exactCenterY();
+        final float cX = mBounds.exactCenterX();
+        final float cY = mBounds.exactCenterY();
+        final float dX = mStartingX - cX;
+        final float dY = mStartingY - cY;
         final float r = mOuterRadius;
         if (dX * dX + dY * dY > r * r) {
             // Point is outside the circle, clamp to the circumference.
             final double angle = Math.atan2(dY, dX);
-            mClampedStartingX = (float) (Math.cos(angle) * r);
-            mClampedStartingY = (float) (Math.sin(angle) * r);
+            mClampedStartingX = cX + (float) (Math.cos(angle) * r);
+            mClampedStartingY = cY + (float) (Math.sin(angle) * r);
         } else {
             mClampedStartingX = mStartingX;
             mClampedStartingY = mStartingY;
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index 0512ecc..f2e75a5 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -645,25 +645,29 @@
 
     @Override
     public Rect getDirtyBounds() {
-        final Rect drawingBounds = mDrawingBounds;
-        final Rect dirtyBounds = mDirtyBounds;
-        dirtyBounds.set(drawingBounds);
-        drawingBounds.setEmpty();
+        if (isProjected()) {
+            final Rect drawingBounds = mDrawingBounds;
+            final Rect dirtyBounds = mDirtyBounds;
+            dirtyBounds.set(drawingBounds);
+            drawingBounds.setEmpty();
 
-        final int cX = (int) mHotspotBounds.exactCenterX();
-        final int cY = (int) mHotspotBounds.exactCenterY();
-        final Rect rippleBounds = mTempRect;
-        final Ripple[] activeRipples = mAnimatingRipples;
-        final int N = mAnimatingRipplesCount;
-        for (int i = 0; i < N; i++) {
-            activeRipples[i].getBounds(rippleBounds);
-            rippleBounds.offset(cX, cY);
-            drawingBounds.union(rippleBounds);
+            final int cX = (int) mHotspotBounds.exactCenterX();
+            final int cY = (int) mHotspotBounds.exactCenterY();
+            final Rect rippleBounds = mTempRect;
+            final Ripple[] activeRipples = mAnimatingRipples;
+            final int N = mAnimatingRipplesCount;
+            for (int i = 0; i < N; i++) {
+                activeRipples[i].getBounds(rippleBounds);
+                rippleBounds.offset(cX, cY);
+                drawingBounds.union(rippleBounds);
+            }
+
+            dirtyBounds.union(drawingBounds);
+            dirtyBounds.union(super.getDirtyBounds());
+            return dirtyBounds;
+        } else {
+            return getBounds();
         }
-
-        dirtyBounds.union(drawingBounds);
-        dirtyBounds.union(super.getDirtyBounds());
-        return dirtyBounds;
     }
 
     @Override
diff --git a/graphics/java/android/graphics/drawable/ShapeDrawable.java b/graphics/java/android/graphics/drawable/ShapeDrawable.java
index beb300d..86765dd 100644
--- a/graphics/java/android/graphics/drawable/ShapeDrawable.java
+++ b/graphics/java/android/graphics/drawable/ShapeDrawable.java
@@ -396,6 +396,9 @@
                         " for ShapeDrawable " + this);
             }
         }
+
+        // Update local properties.
+        initializeWithState(mShapeState, r);
     }
 
     @Override
@@ -410,12 +413,18 @@
         final TypedArray a = t.resolveAttributes(state.mThemeAttrs, R.styleable.ShapeDrawable);
         updateStateFromTypedArray(a);
         a.recycle();
+
+        // Update local properties.
+        initializeWithState(state, t.getResources());
     }
 
     private void updateStateFromTypedArray(TypedArray a) {
         final ShapeState state = mShapeState;
         final Paint paint = state.mPaint;
 
+        // Extract the theme attributes, if any.
+        state.mThemeAttrs = a.extractThemeAttrs();
+
         int color = paint.getColor();
         color = a.getColor(R.styleable.ShapeDrawable_color, color);
         paint.setColor(color);
@@ -428,6 +437,16 @@
                 R.styleable.ShapeDrawable_width, state.mIntrinsicWidth));
         setIntrinsicHeight((int) a.getDimension(
                 R.styleable.ShapeDrawable_height, state.mIntrinsicHeight));
+
+        final int tintMode = a.getInt(R.styleable.ShapeDrawable_tintMode, -1);
+        if (tintMode != -1) {
+            state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
+        }
+
+        final ColorStateList tint = a.getColorStateList(R.styleable.ShapeDrawable_tint);
+        if (tint != null) {
+            state.mTint = tint;
+        }
     }
 
     private void updateShape() {
@@ -542,6 +561,10 @@
         }
     }
 
+    /**
+     * The one constructor to rule them all. This is called by all public
+     * constructors to set the state and initialize local properties.
+     */
     private ShapeDrawable(ShapeState state, Resources res, Theme theme) {
         if (theme != null && state.canApplyTheme()) {
             mShapeState = new ShapeState(state);
@@ -550,7 +573,16 @@
             mShapeState = state;
         }
 
-        mTintFilter = updateTintFilter(mTintFilter, mShapeState.mTint, mShapeState.mTintMode);
+        initializeWithState(state, res);
+    }
+
+    /**
+     * Initializes local dynamic properties from state. This should be called
+     * after significant state changes, e.g. from the One True Constructor and
+     * after inflating or applying a theme.
+     */
+    private void initializeWithState(ShapeState state, Resources res) {
+        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
     }
 
     /**
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index c3c1bca..c98f2a1 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -33,6 +33,7 @@
 import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
+import android.util.PathParser;
 import android.util.Xml;
 
 import com.android.internal.R;
@@ -147,10 +148,12 @@
     }
 
     private VectorDrawable(VectorDrawableState state, Resources res, Theme theme) {
-        mVectorState = new VectorDrawableState(state);
-
-        if (theme != null && canApplyTheme()) {
+        if (theme != null && state.canApplyTheme()) {
+            // If we need to apply a theme, implicitly mutate.
+            mVectorState = new VectorDrawableState(state);
             applyTheme(theme);
+        } else {
+            mVectorState = state;
         }
 
         mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
@@ -234,34 +237,6 @@
         return PixelFormat.TRANSLUCENT;
     }
 
-    /**
-     * Sets padding for this shape, defined by a Rect object. Define the padding
-     * in the Rect object as: left, top, right, bottom.
-     */
-    public void setPadding(Rect padding) {
-        setPadding(padding.left, padding.top, padding.right, padding.bottom);
-    }
-
-    /**
-     * Sets padding for the shape.
-     *
-     * @param left padding for the left side (in pixels)
-     * @param top padding for the top (in pixels)
-     * @param right padding for the right side (in pixels)
-     * @param bottom padding for the bottom (in pixels)
-     */
-    public void setPadding(int left, int top, int right, int bottom) {
-        if ((left | top | right | bottom) == 0) {
-            mVectorState.mPadding = null;
-        } else {
-            if (mVectorState.mPadding == null) {
-                mVectorState.mPadding = new Rect();
-            }
-            mVectorState.mPadding.set(left, top, right, bottom);
-        }
-        invalidateSelf();
-    }
-
     @Override
     public int getIntrinsicWidth() {
         return (int) mVectorState.mVPathRenderer.mBaseWidth;
@@ -273,23 +248,6 @@
     }
 
     @Override
-    public boolean getPadding(Rect padding) {
-        if (mVectorState.mPadding != null) {
-            padding.set(mVectorState.mPadding);
-            return true;
-        } else {
-            return super.getPadding(padding);
-        }
-    }
-
-    @Override
-    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
-            throws XmlPullParserException, IOException {
-        final VPathRenderer p = inflateInternal(res, parser, attrs, theme);
-        setPathRenderer(p);
-    }
-
-    @Override
     public boolean canApplyTheme() {
         return super.canApplyTheme() || mVectorState != null && mVectorState.canApplyTheme();
     }
@@ -332,13 +290,44 @@
         return color;
     }
 
-    private VPathRenderer inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs,
-            Theme theme) throws XmlPullParserException, IOException {
+
+    @Override
+    public void inflate(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
+            throws XmlPullParserException, IOException {
+        final TypedArray a = obtainAttributes(res, theme,  attrs,R.styleable.VectorDrawable);
+        updateStateFromTypedArray(a);
+        a.recycle();
+
+        final VectorDrawableState state = mVectorState;
+        state.mVPathRenderer = inflateInternal(res, parser, attrs, theme);
+
+        mTintFilter = updateTintFilter(mTintFilter, state.mTint, state.mTintMode);
+        state.mVPathRenderer.setColorFilter(mTintFilter);
+    }
+
+    private void updateStateFromTypedArray(TypedArray a) {
+        final VectorDrawableState state = mVectorState;
+
+        // Extract the theme attributes, if any.
+        state.mThemeAttrs = a.extractThemeAttrs();
+
+        final int tintMode = a.getInt(R.styleable.VectorDrawable_tintMode, -1);
+        if (tintMode != -1) {
+            state.mTintMode = Drawable.parseTintMode(tintMode, Mode.SRC_IN);
+        }
+
+        final ColorStateList tint = a.getColorStateList(R.styleable.VectorDrawable_tint);
+        if (tint != null) {
+            state.mTint = tint;
+        }
+    }
+
+    private VPathRenderer inflateInternal(Resources res, XmlPullParser parser, AttributeSet attrs, Theme theme)
+            throws XmlPullParserException, IOException {
         final VPathRenderer pathRenderer = new VPathRenderer();
 
         boolean noSizeTag = true;
         boolean noViewportTag = true;
-        boolean noGroupTag = true;
         boolean noPathTag = true;
 
         // Use a stack to help to build the group tree.
@@ -374,7 +363,6 @@
                     if (newChildGroup.getGroupName() != null) {
                         mVGTargetsMap.put(newChildGroup.getGroupName(), newChildGroup);
                     }
-                    noGroupTag = false;
                 }
             } else if (eventType == XmlPullParser.END_TAG) {
                 final String tagName = parser.getName();
@@ -432,23 +420,19 @@
         }
     }
 
-    private void setPathRenderer(VPathRenderer pathRenderer) {
-        mVectorState.mVPathRenderer = pathRenderer;
-    }
-
     private static class VectorDrawableState extends ConstantState {
+        int[] mThemeAttrs;
         int mChangingConfigurations;
         VPathRenderer mVPathRenderer;
-        Rect mPadding;
         ColorStateList mTint;
         Mode mTintMode;
 
         public VectorDrawableState(VectorDrawableState copy) {
             if (copy != null) {
+                mThemeAttrs = copy.mThemeAttrs;
                 mChangingConfigurations = copy.mChangingConfigurations;
                 // TODO: Make sure the constant state are handled correctly.
                 mVPathRenderer = new VPathRenderer(copy.mVPathRenderer);
-                mPadding = new Rect(copy.mPadding);
                 mTint = copy.mTint;
                 mTintMode = copy.mTintMode;
             }
@@ -954,7 +938,7 @@
 
     }
 
-    static class VPath {
+    private static class VPath {
         private int[] mThemeAttrs;
 
         int mStrokeColor = 0;
@@ -972,7 +956,7 @@
         Paint.Join mStrokeLineJoin = Paint.Join.MITER;
         float mStrokeMiterlimit = 4;
 
-        private VNode[] mNode = null;
+        private PathParser.PathDataNode[] mNode = null;
         private String mPathName;
 
         public VPath() {
@@ -982,7 +966,7 @@
         public void toPath(Path path) {
             path.reset();
             if (mNode != null) {
-                VNode.createPath(mNode, path);
+                PathParser.PathDataNode.nodesToPath(mNode, path);
             }
         }
 
@@ -1016,67 +1000,98 @@
             }
         }
 
-        /* Setters and Getters */
+        /* Setters and Getters, mostly used by animator from AnimatedVectorDrawable. */
+        @SuppressWarnings("unused")
+        public PathParser.PathDataNode[] getPathData() {
+            return mNode;
+        }
+
+        @SuppressWarnings("unused")
+        public void setPathData(PathParser.PathDataNode[] node) {
+            if (!PathParser.canMorph(mNode, node)) {
+                // This should not happen in the middle of animation.
+                mNode = PathParser.deepCopyNodes(node);
+            } else {
+                PathParser.updateNodes(mNode, node);
+            }
+        }
+
+        @SuppressWarnings("unused")
         int getStroke() {
             return mStrokeColor;
         }
 
+        @SuppressWarnings("unused")
         void setStroke(int strokeColor) {
             mStrokeColor = strokeColor;
         }
 
+        @SuppressWarnings("unused")
         float getStrokeWidth() {
             return mStrokeWidth;
         }
 
+        @SuppressWarnings("unused")
         void setStrokeWidth(float strokeWidth) {
             mStrokeWidth = strokeWidth;
         }
 
+        @SuppressWarnings("unused")
         float getStrokeOpacity() {
             return mStrokeOpacity;
         }
 
+        @SuppressWarnings("unused")
         void setStrokeOpacity(float strokeOpacity) {
             mStrokeOpacity = strokeOpacity;
         }
 
+        @SuppressWarnings("unused")
         int getFill() {
             return mFillColor;
         }
 
+        @SuppressWarnings("unused")
         void setFill(int fillColor) {
             mFillColor = fillColor;
         }
 
+        @SuppressWarnings("unused")
         float getFillOpacity() {
             return mFillOpacity;
         }
 
+        @SuppressWarnings("unused")
         void setFillOpacity(float fillOpacity) {
             mFillOpacity = fillOpacity;
         }
 
+        @SuppressWarnings("unused")
         float getTrimPathStart() {
             return mTrimPathStart;
         }
 
+        @SuppressWarnings("unused")
         void setTrimPathStart(float trimPathStart) {
             mTrimPathStart = trimPathStart;
         }
 
+        @SuppressWarnings("unused")
         float getTrimPathEnd() {
             return mTrimPathEnd;
         }
 
+        @SuppressWarnings("unused")
         void setTrimPathEnd(float trimPathEnd) {
             mTrimPathEnd = trimPathEnd;
         }
 
+        @SuppressWarnings("unused")
         float getTrimPathOffset() {
             return mTrimPathOffset;
         }
 
+        @SuppressWarnings("unused")
         void setTrimPathOffset(float trimPathOffset) {
             mTrimPathOffset = trimPathOffset;
         }
@@ -1097,7 +1112,8 @@
             }
 
             if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_pathData] == 0) {
-                mNode = parsePath(a.getString(R.styleable.VectorDrawablePath_pathData));
+                mNode = PathParser.createNodesFromPathData(a.getString(
+                        R.styleable.VectorDrawablePath_pathData));
             }
 
             if (themeAttrs == null || themeAttrs[R.styleable.VectorDrawablePath_fill] == 0) {
@@ -1180,7 +1196,8 @@
             }
 
             if (a.hasValue(R.styleable.VectorDrawablePath_pathData)) {
-                mNode = parsePath(a.getString(R.styleable.VectorDrawablePath_pathData));
+                mNode = PathParser.createNodesFromPathData(a.getString(
+                        R.styleable.VectorDrawablePath_pathData));
             }
 
             mFillColor = a.getColor(R.styleable.VectorDrawablePath_fill, mFillColor);
@@ -1216,488 +1233,5 @@
                 mStrokeColor = applyAlpha(mStrokeColor, mStrokeOpacity);
             }
         }
-
-        private static int nextStart(String s, int end) {
-            char c;
-
-            while (end < s.length()) {
-                c = s.charAt(end);
-                if (((c - 'A') * (c - 'Z') <= 0) || (((c - 'a') * (c - 'z') <= 0))) {
-                    return end;
-                }
-                end++;
-            }
-            return end;
-        }
-
-        private void addNode(ArrayList<VectorDrawable.VNode> list, char cmd, float[] val) {
-            list.add(new VectorDrawable.VNode(cmd, val));
-        }
-
-        /**
-         * parse the floats in the string
-         * this is an optimized version of
-         * parseFloat(s.split(",|\\s"));
-         *
-         * @param s the string containing a command and list of floats
-         * @return array of floats
-         */
-        private static float[] getFloats(String s) {
-            if (s.charAt(0) == 'z' | s.charAt(0) == 'Z') {
-                return new float[0];
-            }
-            try {
-                float[] tmp = new float[s.length()];
-                int count = 0;
-                int pos = 1, end;
-                while ((end = extract(s, pos)) >= 0) {
-                    if (pos < end) {
-                        tmp[count++] = Float.parseFloat(s.substring(pos, end));
-                    }
-                    pos = end + 1;
-                }
-                // handle the final float if there is one
-                if (pos < s.length()) {
-                    tmp[count++] = Float.parseFloat(s.substring(pos, s.length()));
-                }
-                return Arrays.copyOf(tmp, count);
-            } catch (NumberFormatException e){
-                Log.e(LOGTAG,"error in parsing \""+s+"\"");
-                throw e;
-            }
-        }
-
-        /**
-         * calculate the position of the next comma or space
-         * @param s the string to search
-         * @param start the position to start searching
-         * @return the position of the next comma or space or -1 if none found
-         */
-        private static int extract(String s, int start) {
-            int space = s.indexOf(' ', start);
-            int comma = s.indexOf(',', start);
-            if (space == -1) {
-                return comma;
-            }
-            if (comma == -1) {
-                return space;
-            }
-            return (comma > space) ? space : comma;
-        }
-
-        private VectorDrawable.VNode[] parsePath(String value) {
-            int start = 0;
-            int end = 1;
-
-            ArrayList<VectorDrawable.VNode> list = new ArrayList<VectorDrawable.VNode>();
-            while (end < value.length()) {
-                end = nextStart(value, end);
-                String s = value.substring(start, end);
-                float[] val = getFloats(s);
-                addNode(list, s.charAt(0), val);
-
-                start = end;
-                end++;
-            }
-            if ((end - start) == 1 && start < value.length()) {
-
-                addNode(list, value.charAt(start), new float[0]);
-            }
-            return list.toArray(new VectorDrawable.VNode[list.size()]);
-        }
-    }
-
-    private static class VNode {
-        private char mType;
-        private float[] mParams;
-
-        public VNode(char type, float[] params) {
-            mType = type;
-            mParams = params;
-        }
-
-        public VNode(VNode n) {
-            mType = n.mType;
-            mParams = Arrays.copyOf(n.mParams, n.mParams.length);
-        }
-
-        public static void createPath(VNode[] node, Path path) {
-            float[] current = new float[4];
-            char previousCommand = 'm';
-            for (int i = 0; i < node.length; i++) {
-                addCommand(path, current, previousCommand, node[i].mType, node[i].mParams);
-                previousCommand = node[i].mType;
-            }
-        }
-
-        private static void addCommand(Path path, float[] current,
-                char previousCmd, char cmd, float[] val) {
-
-            int incr = 2;
-            float currentX = current[0];
-            float currentY = current[1];
-            float ctrlPointX = current[2];
-            float ctrlPointY = current[3];
-            float reflectiveCtrlPointX;
-            float reflectiveCtrlPointY;
-
-            switch (cmd) {
-                case 'z':
-                case 'Z':
-                    path.close();
-                    return;
-                case 'm':
-                case 'M':
-                case 'l':
-                case 'L':
-                case 't':
-                case 'T':
-                    incr = 2;
-                    break;
-                case 'h':
-                case 'H':
-                case 'v':
-                case 'V':
-                    incr = 1;
-                    break;
-                case 'c':
-                case 'C':
-                    incr = 6;
-                    break;
-                case 's':
-                case 'S':
-                case 'q':
-                case 'Q':
-                    incr = 4;
-                    break;
-                case 'a':
-                case 'A':
-                    incr = 7;
-                    break;
-            }
-            for (int k = 0; k < val.length; k += incr) {
-                switch (cmd) {
-                    case 'm': // moveto - Start a new sub-path (relative)
-                        path.rMoveTo(val[k + 0], val[k + 1]);
-                        currentX += val[k + 0];
-                        currentY += val[k + 1];
-                        break;
-                    case 'M': // moveto - Start a new sub-path
-                        path.moveTo(val[k + 0], val[k + 1]);
-                        currentX = val[k + 0];
-                        currentY = val[k + 1];
-                        break;
-                    case 'l': // lineto - Draw a line from the current point (relative)
-                        path.rLineTo(val[k + 0], val[k + 1]);
-                        currentX += val[k + 0];
-                        currentY += val[k + 1];
-                        break;
-                    case 'L': // lineto - Draw a line from the current point
-                        path.lineTo(val[k + 0], val[k + 1]);
-                        currentX = val[k + 0];
-                        currentY = val[k + 1];
-                        break;
-                    case 'z': // closepath - Close the current subpath
-                    case 'Z': // closepath - Close the current subpath
-                        path.close();
-                        break;
-                    case 'h': // horizontal lineto - Draws a horizontal line (relative)
-                        path.rLineTo(val[k + 0], 0);
-                        currentX += val[k + 0];
-                        break;
-                    case 'H': // horizontal lineto - Draws a horizontal line
-                        path.lineTo(val[k + 0], currentY);
-                        currentX = val[k + 0];
-                        break;
-                    case 'v': // vertical lineto - Draws a vertical line from the current point (r)
-                        path.rLineTo(0, val[k + 0]);
-                        currentY += val[k + 0];
-                        break;
-                    case 'V': // vertical lineto - Draws a vertical line from the current point
-                        path.lineTo(currentX, val[k + 0]);
-                        currentY = val[k + 0];
-                        break;
-                    case 'c': // curveto - Draws a cubic Bézier curve (relative)
-                        path.rCubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
-                                val[k + 4], val[k + 5]);
-
-                        ctrlPointX = currentX + val[k + 2];
-                        ctrlPointY = currentY + val[k + 3];
-                        currentX += val[k + 4];
-                        currentY += val[k + 5];
-
-                        break;
-                    case 'C': // curveto - Draws a cubic Bézier curve
-                        path.cubicTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3],
-                                val[k + 4], val[k + 5]);
-                        currentX = val[k + 4];
-                        currentY = val[k + 5];
-                        ctrlPointX = val[k + 2];
-                        ctrlPointY = val[k + 3];
-                        break;
-                    case 's': // smooth curveto - Draws a cubic Bézier curve (reflective cp)
-                        reflectiveCtrlPointX = 0;
-                        reflectiveCtrlPointY = 0;
-                        if (previousCmd == 'c' || previousCmd == 's'
-                                || previousCmd == 'C' || previousCmd == 'S') {
-                            reflectiveCtrlPointX = currentX - ctrlPointX;
-                            reflectiveCtrlPointY = currentY - ctrlPointY;
-                        }
-                        path.rCubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1],
-                                val[k + 2], val[k + 3]);
-
-                        ctrlPointX = currentX + val[k + 0];
-                        ctrlPointY = currentY + val[k + 1];
-                        currentX += val[k + 2];
-                        currentY += val[k + 3];
-                        break;
-                    case 'S': // shorthand/smooth curveto Draws a cubic Bézier curve(reflective cp)
-                        reflectiveCtrlPointX = currentX;
-                        reflectiveCtrlPointY = currentY;
-                        if (previousCmd == 'c' || previousCmd == 's'
-                                || previousCmd == 'C' || previousCmd == 'S') {
-                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
-                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
-                        }
-                        path.cubicTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
-                        ctrlPointX = val[k + 0];
-                        ctrlPointY = val[k + 1];
-                        currentX = val[k + 2];
-                        currentY = val[k + 3];
-                        break;
-                    case 'q': // Draws a quadratic Bézier (relative)
-                        path.rQuadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
-                        ctrlPointX = currentX + val[k + 0];
-                        ctrlPointY = currentY + val[k + 1];
-                        currentX += val[k + 2];
-                        currentY += val[k + 3];
-                        break;
-                    case 'Q': // Draws a quadratic Bézier
-                        path.quadTo(val[k + 0], val[k + 1], val[k + 2], val[k + 3]);
-                        ctrlPointX = val[k + 0];
-                        ctrlPointY = val[k + 1];
-                        currentX = val[k + 2];
-                        currentY = val[k + 3];
-                        break;
-                    case 't': // Draws a quadratic Bézier curve(reflective control point)(relative)
-                        reflectiveCtrlPointX = 0;
-                        reflectiveCtrlPointY = 0;
-                        if (previousCmd == 'q' || previousCmd == 't'
-                                || previousCmd == 'Q' || previousCmd == 'T') {
-                            reflectiveCtrlPointX = currentX - ctrlPointX;
-                            reflectiveCtrlPointY = currentY - ctrlPointY;
-                        }
-                        path.rQuadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1]);
-                        ctrlPointX = currentX + reflectiveCtrlPointX;
-                        ctrlPointY = currentY + reflectiveCtrlPointY;
-                        currentX += val[k + 0];
-                        currentY += val[k + 1];
-                        break;
-                    case 'T': // Draws a quadratic Bézier curve (reflective control point)
-                        reflectiveCtrlPointX = currentX;
-                        reflectiveCtrlPointY = currentY;
-                        if (previousCmd == 'q' || previousCmd == 't'
-                                || previousCmd == 'Q' || previousCmd == 'T') {
-                            reflectiveCtrlPointX = 2 * currentX - ctrlPointX;
-                            reflectiveCtrlPointY = 2 * currentY - ctrlPointY;
-                        }
-                        path.quadTo(reflectiveCtrlPointX, reflectiveCtrlPointY,
-                                val[k + 0], val[k + 1]);
-                        ctrlPointX = reflectiveCtrlPointX;
-                        ctrlPointY = reflectiveCtrlPointY;
-                        currentX = val[k + 0];
-                        currentY = val[k + 1];
-                        break;
-                    case 'a': // Draws an elliptical arc
-                        // (rx ry x-axis-rotation large-arc-flag sweep-flag x y)
-                        drawArc(path,
-                                currentX,
-                                currentY,
-                                val[k + 5] + currentX,
-                                val[k + 6] + currentY,
-                                val[k + 0],
-                                val[k + 1],
-                                val[k + 2],
-                                val[k + 3] != 0,
-                                val[k + 4] != 0);
-                        currentX += val[k + 5];
-                        currentY += val[k + 6];
-                        ctrlPointX = currentX;
-                        ctrlPointY = currentY;
-                        break;
-                    case 'A': // Draws an elliptical arc
-                        drawArc(path,
-                                currentX,
-                                currentY,
-                                val[k + 5],
-                                val[k + 6],
-                                val[k + 0],
-                                val[k + 1],
-                                val[k + 2],
-                                val[k + 3] != 0,
-                                val[k + 4] != 0);
-                        currentX = val[k + 5];
-                        currentY = val[k + 6];
-                        ctrlPointX = currentX;
-                        ctrlPointY = currentY;
-                        break;
-                }
-                previousCmd = cmd;
-            }
-            current[0] = currentX;
-            current[1] = currentY;
-            current[2] = ctrlPointX;
-            current[3] = ctrlPointY;
-        }
-
-        private static void drawArc(Path p,
-                float x0,
-                float y0,
-                float x1,
-                float y1,
-                float a,
-                float b,
-                float theta,
-                boolean isMoreThanHalf,
-                boolean isPositiveArc) {
-
-            /* Convert rotation angle from degrees to radians */
-            double thetaD = Math.toRadians(theta);
-            /* Pre-compute rotation matrix entries */
-            double cosTheta = Math.cos(thetaD);
-            double sinTheta = Math.sin(thetaD);
-            /* Transform (x0, y0) and (x1, y1) into unit space */
-            /* using (inverse) rotation, followed by (inverse) scale */
-            double x0p = (x0 * cosTheta + y0 * sinTheta) / a;
-            double y0p = (-x0 * sinTheta + y0 * cosTheta) / b;
-            double x1p = (x1 * cosTheta + y1 * sinTheta) / a;
-            double y1p = (-x1 * sinTheta + y1 * cosTheta) / b;
-
-            /* Compute differences and averages */
-            double dx = x0p - x1p;
-            double dy = y0p - y1p;
-            double xm = (x0p + x1p) / 2;
-            double ym = (y0p + y1p) / 2;
-            /* Solve for intersecting unit circles */
-            double dsq = dx * dx + dy * dy;
-            if (dsq == 0.0) {
-                Log.w(LOGTAG, " Points are coincident");
-                return; /* Points are coincident */
-            }
-            double disc = 1.0 / dsq - 1.0 / 4.0;
-            if (disc < 0.0) {
-                Log.w(LOGTAG, "Points are too far apart " + dsq);
-                float adjust = (float) (Math.sqrt(dsq) / 1.99999);
-                drawArc(p, x0, y0, x1, y1, a * adjust,
-                        b * adjust, theta, isMoreThanHalf, isPositiveArc);
-                return; /* Points are too far apart */
-            }
-            double s = Math.sqrt(disc);
-            double sdx = s * dx;
-            double sdy = s * dy;
-            double cx;
-            double cy;
-            if (isMoreThanHalf == isPositiveArc) {
-                cx = xm - sdy;
-                cy = ym + sdx;
-            } else {
-                cx = xm + sdy;
-                cy = ym - sdx;
-            }
-
-            double eta0 = Math.atan2((y0p - cy), (x0p - cx));
-
-            double eta1 = Math.atan2((y1p - cy), (x1p - cx));
-
-            double sweep = (eta1 - eta0);
-            if (isPositiveArc != (sweep >= 0)) {
-                if (sweep > 0) {
-                    sweep -= 2 * Math.PI;
-                } else {
-                    sweep += 2 * Math.PI;
-                }
-            }
-
-            cx *= a;
-            cy *= b;
-            double tcx = cx;
-            cx = cx * cosTheta - cy * sinTheta;
-            cy = tcx * sinTheta + cy * cosTheta;
-
-            arcToBezier(p, cx, cy, a, b, x0, y0, thetaD, eta0, sweep);
-        }
-
-        /**
-         * Converts an arc to cubic Bezier segments and records them in p.
-         *
-         * @param p The target for the cubic Bezier segments
-         * @param cx The x coordinate center of the ellipse
-         * @param cy The y coordinate center of the ellipse
-         * @param a The radius of the ellipse in the horizontal direction
-         * @param b The radius of the ellipse in the vertical direction
-         * @param e1x E(eta1) x coordinate of the starting point of the arc
-         * @param e1y E(eta2) y coordinate of the starting point of the arc
-         * @param theta The angle that the ellipse bounding rectangle makes with horizontal plane
-         * @param start The start angle of the arc on the ellipse
-         * @param sweep The angle (positive or negative) of the sweep of the arc on the ellipse
-         */
-        private static void arcToBezier(Path p,
-                double cx,
-                double cy,
-                double a,
-                double b,
-                double e1x,
-                double e1y,
-                double theta,
-                double start,
-                double sweep) {
-            // Taken from equations at: http://spaceroots.org/documents/ellipse/node8.html
-            // and http://www.spaceroots.org/documents/ellipse/node22.html
-
-            // Maximum of 45 degrees per cubic Bezier segment
-            int numSegments = Math.abs((int) Math.ceil(sweep * 4 / Math.PI));
-
-            double eta1 = start;
-            double cosTheta = Math.cos(theta);
-            double sinTheta = Math.sin(theta);
-            double cosEta1 = Math.cos(eta1);
-            double sinEta1 = Math.sin(eta1);
-            double ep1x = (-a * cosTheta * sinEta1) - (b * sinTheta * cosEta1);
-            double ep1y = (-a * sinTheta * sinEta1) + (b * cosTheta * cosEta1);
-
-            double anglePerSegment = sweep / numSegments;
-            for (int i = 0; i < numSegments; i++) {
-                double eta2 = eta1 + anglePerSegment;
-                double sinEta2 = Math.sin(eta2);
-                double cosEta2 = Math.cos(eta2);
-                double e2x = cx + (a * cosTheta * cosEta2) - (b * sinTheta * sinEta2);
-                double e2y = cy + (a * sinTheta * cosEta2) + (b * cosTheta * sinEta2);
-                double ep2x = -a * cosTheta * sinEta2 - b * sinTheta * cosEta2;
-                double ep2y = -a * sinTheta * sinEta2 + b * cosTheta * cosEta2;
-                double tanDiff2 = Math.tan((eta2 - eta1) / 2);
-                double alpha =
-                        Math.sin(eta2 - eta1) * (Math.sqrt(4 + (3 * tanDiff2 * tanDiff2)) - 1) / 3;
-                double q1x = e1x + alpha * ep1x;
-                double q1y = e1y + alpha * ep1y;
-                double q2x = e2x - alpha * ep2x;
-                double q2y = e2y - alpha * ep2y;
-
-                p.cubicTo((float) q1x,
-                        (float) q1y,
-                        (float) q2x,
-                        (float) q2y,
-                        (float) e2x,
-                        (float) e2y);
-                eta1 = eta2;
-                e1x = e2x;
-                e1y = e2y;
-                ep1x = ep2x;
-                ep1y = ep2y;
-            }
-        }
-
     }
 }
diff --git a/include/androidfw/ByteBucketArray.h b/include/androidfw/ByteBucketArray.h
new file mode 100644
index 0000000..87c6b12
--- /dev/null
+++ b/include/androidfw/ByteBucketArray.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __BYTE_BUCKET_ARRAY_H
+#define __BYTE_BUCKET_ARRAY_H
+
+#include <utils/Log.h>
+#include <stdint.h>
+#include <string.h>
+
+namespace android {
+
+/**
+ * Stores a sparsely populated array. Has a fixed size of 256
+ * (number of entries that a byte can represent).
+ */
+template<typename T>
+class ByteBucketArray {
+public:
+    ByteBucketArray() : mDefault() {
+        memset(mBuckets, 0, sizeof(mBuckets));
+    }
+
+    ~ByteBucketArray() {
+        for (size_t i = 0; i < NUM_BUCKETS; i++) {
+            if (mBuckets[i] != NULL) {
+                delete [] mBuckets[i];
+            }
+        }
+        memset(mBuckets, 0, sizeof(mBuckets));
+    }
+
+    inline size_t size() const {
+        return NUM_BUCKETS * BUCKET_SIZE;
+    }
+
+    inline const T& get(size_t index) const {
+        return (*this)[index];
+    }
+
+    const T& operator[](size_t index) const {
+        if (index >= size()) {
+            return mDefault;
+        }
+
+        uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
+        T* bucket = mBuckets[bucketIndex];
+        if (bucket == NULL) {
+            return mDefault;
+        }
+        return bucket[0x0f & static_cast<uint8_t>(index)];
+    }
+
+    T& editItemAt(size_t index) {
+        ALOG_ASSERT(index < size(), "ByteBucketArray.getOrCreate(index=%u) with size=%u",
+                (uint32_t) index, (uint32_t) size());
+
+        uint8_t bucketIndex = static_cast<uint8_t>(index) >> 4;
+        T* bucket = mBuckets[bucketIndex];
+        if (bucket == NULL) {
+            bucket = mBuckets[bucketIndex] = new T[BUCKET_SIZE]();
+        }
+        return bucket[0x0f & static_cast<uint8_t>(index)];
+    }
+
+    bool set(size_t index, const T& value) {
+        if (index >= size()) {
+            return false;
+        }
+
+        editItemAt(index) = value;
+        return true;
+    }
+
+private:
+    enum { NUM_BUCKETS = 16, BUCKET_SIZE = 16 };
+
+    T*  mBuckets[NUM_BUCKETS];
+    T   mDefault;
+};
+
+} // namespace android
+
+#endif // __BYTE_BUCKET_ARRAY_H
diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h
index 4d8e512..e612c0a 100644
--- a/include/androidfw/ResourceTypes.h
+++ b/include/androidfw/ResourceTypes.h
@@ -237,6 +237,7 @@
 #define Res_MAKEARRAY(entry) (0x02000000 | (entry&0xFFFF))
 
 #define Res_MAXPACKAGE 255
+#define Res_MAXTYPE 255
 
 /**
  * Representation of a value in a resource, supplying type
@@ -510,6 +511,23 @@
     uint32_t                    mStylePoolSize;    // number of uint32_t
 };
 
+/**
+ * Wrapper class that allows the caller to retrieve a string from
+ * a string pool without knowing which string pool to look.
+ */
+class StringPoolRef {
+public:
+    StringPoolRef();
+    StringPoolRef(const ResStringPool* pool, uint32_t index);
+
+    const char* string8(size_t* outLen) const;
+    const char16_t* string16(size_t* outLen) const;
+
+private:
+    const ResStringPool*        mPool;
+    uint32_t                    mIndex;
+};
+
 /** ********************************************************************
  *  XML Tree
  *
@@ -835,6 +853,8 @@
 
     // Last index into keyStrings that is for public use by others.
     uint32_t lastPublicKey;
+
+    uint32_t typeIdOffset;
 };
 
 // The most specific locale can consist of:
@@ -1469,9 +1489,13 @@
              bool copyData=false);
     ~ResTable();
 
-    status_t add(Asset* asset, const int32_t cookie, bool copyData,
-                 const void* idmap = NULL);
-    status_t add(const void *data, size_t size);
+    status_t add(const void* data, size_t size, const int32_t cookie=-1, bool copyData=false);
+    status_t add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
+            const int32_t cookie=-1, bool copyData=false);
+
+    status_t add(Asset* asset, const int32_t cookie=-1, bool copyData=false);
+    status_t add(Asset* asset, Asset* idmapAsset, const int32_t cookie=-1, bool copyData=false);
+
     status_t add(ResTable* src);
     status_t addEmpty(const int32_t cookie);
 
@@ -1610,13 +1634,14 @@
             uint32_t typeSpecFlags;
             Res_value value;
         };
+
         struct type_info {
             size_t numEntries;
             theme_entry* entries;
         };
+
         struct package_info {
-            size_t numTypes;
-            type_info types[];
+            type_info types[Res_MAXTYPE + 1];
         };
 
         void free_package(package_info* pi);
@@ -1711,6 +1736,7 @@
     size_t getBasePackageCount() const;
     const String16 getBasePackageName(size_t idx) const;
     uint32_t getBasePackageId(size_t idx) const;
+    uint32_t getLastTypeIdForPackage(size_t idx) const;
 
     // Return the number of resource tables that the object contains.
     size_t getTableCount() const;
@@ -1740,13 +1766,15 @@
             void** outData, size_t* outSize) const;
 
     enum {
-        IDMAP_HEADER_SIZE_BYTES = 3 * sizeof(uint32_t) + 2 * 256,
+        IDMAP_HEADER_SIZE_BYTES = 4 * sizeof(uint32_t) + 2 * 256,
     };
+
     // Retrieve idmap meta-data.
     //
     // This function only requires the idmap header (the first
     // IDMAP_HEADER_SIZE_BYTES) bytes of an idmap file.
     static bool getIdmapInfo(const void* idmap, size_t size,
+            uint32_t* pVersion,
             uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
             String8* pTargetPath, String8* pOverlayPath);
 
@@ -1756,21 +1784,24 @@
 private:
     struct Header;
     struct Type;
+    struct Entry;
     struct Package;
     struct PackageGroup;
     struct bag_set;
+    typedef Vector<Type*> TypeList;
 
-    status_t addInternal(const void* data, size_t size, const int32_t cookie,
-                 bool copyData, const Asset* idmap);
+    status_t addInternal(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
+            const int32_t cookie, bool copyData);
 
     ssize_t getResourcePackageIndex(uint32_t resID) const;
-    ssize_t getEntry(
-        const Package* package, int typeIndex, int entryIndex,
+
+    status_t getEntry(
+        const PackageGroup* packageGroup, int typeIndex, int entryIndex,
         const ResTable_config* config,
-        const ResTable_type** outType, const ResTable_entry** outEntry,
-        const Type** outTypeClass) const;
+        Entry* outEntry) const;
+
     status_t parsePackage(
-        const ResTable_package* const pkg, const Header* const header, uint32_t idmap_id);
+        const ResTable_package* const pkg, const Header* const header);
 
     void print_value(const Package* pkg, const Res_value& value) const;
     
diff --git a/include/androidfw/TypeWrappers.h b/include/androidfw/TypeWrappers.h
new file mode 100644
index 0000000..7bdf8af
--- /dev/null
+++ b/include/androidfw/TypeWrappers.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef __TYPE_WRAPPERS_H
+#define __TYPE_WRAPPERS_H
+
+#include <androidfw/ResourceTypes.h>
+
+namespace android {
+
+struct TypeVariant {
+    TypeVariant(const ResTable_type* data)
+        : data(data) {}
+
+    class iterator {
+    public:
+        iterator& operator=(const iterator& rhs) {
+            mTypeVariant = rhs.mTypeVariant;
+            mIndex = rhs.mIndex;
+        }
+
+        bool operator==(const iterator& rhs) const {
+            return mTypeVariant == rhs.mTypeVariant && mIndex == rhs.mIndex;
+        }
+
+        bool operator!=(const iterator& rhs) const {
+            return mTypeVariant != rhs.mTypeVariant || mIndex != rhs.mIndex;
+        }
+
+        iterator operator++(int) {
+            uint32_t prevIndex = mIndex;
+            operator++();
+            return iterator(mTypeVariant, prevIndex);
+        }
+
+        const ResTable_entry* operator->() const {
+            return operator*();
+        }
+
+        uint32_t index() const {
+            return mIndex;
+        }
+
+        iterator& operator++();
+        const ResTable_entry* operator*() const;
+
+    private:
+        friend struct TypeVariant;
+        iterator(const TypeVariant* tv, uint32_t index)
+            : mTypeVariant(tv), mIndex(index) {}
+        const TypeVariant* mTypeVariant;
+        uint32_t mIndex;
+    };
+
+    iterator beginEntries() const {
+        return iterator(this, 0);
+    }
+
+    iterator endEntries() const {
+        return iterator(this, dtohl(data->entryCount));
+    }
+
+    const ResTable_type* data;
+};
+
+} // namespace android
+
+#endif // __TYPE_WRAPPERS_H
diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk
index d21197e..957809d 100644
--- a/libs/androidfw/Android.mk
+++ b/libs/androidfw/Android.mk
@@ -25,6 +25,7 @@
     ObbFile.cpp \
     ResourceTypes.cpp \
     StreamingZipInflater.cpp \
+    TypeWrappers.cpp \
     ZipFileRO.cpp \
     ZipUtils.cpp
 
diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp
index 1b3f1fd..0340928 100644
--- a/libs/androidfw/AssetManager.cpp
+++ b/libs/androidfw/AssetManager.cpp
@@ -256,7 +256,7 @@
     String8 targetPath;
     String8 overlayPath;
     if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(),
-                NULL, NULL, &targetPath, &overlayPath)) {
+                NULL, NULL, NULL, &targetPath, &overlayPath)) {
         ALOGW("failed to read idmap file %s\n", idmapPath.string());
         delete idmap;
         return false;
@@ -311,7 +311,7 @@
             ALOGW("failed to find resources.arsc in %s\n", ap.path.string());
             return false;
         }
-        tables[i].add(ass, 1, false /* copyData */, NULL /* idMap */);
+        tables[i].add(ass);
     }
 
     return tables[0].createIdmap(tables[1], targetCrc, overlayCrc,
@@ -617,7 +617,7 @@
                     // can quickly copy it out for others.
                     ALOGV("Creating shared resources for %s", ap.path.string());
                     sharedRes = new ResTable();
-                    sharedRes->add(ass, i + 1, false, idmap);
+                    sharedRes->add(ass, idmap, i + 1, false);
 #ifdef HAVE_ANDROID_OS
                     const char* data = getenv("ANDROID_DATA");
                     LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set");
@@ -646,7 +646,7 @@
                 mResources->add(sharedRes);
             } else {
                 ALOGV("Parsing resources for %s", ap.path.string());
-                mResources->add(ass, i + 1, !shared, idmap);
+                mResources->add(ass, idmap, i + 1, !shared);
             }
             onlyEmptyResources = false;
 
@@ -654,7 +654,7 @@
                 delete ass;
             }
         } else {
-            ALOGW("Installing empty resources in to table %p\n", mResources);
+            ALOGV("Installing empty resources in to table %p\n", mResources);
             mResources->addEmpty(i + 1);
         }
 
@@ -736,7 +736,7 @@
         if (oass != NULL) {
             Asset* oidmap = openIdmapLocked(oap);
             offset++;
-            sharedRes->add(oass, offset + 1, false, oidmap);
+            sharedRes->add(oass, oidmap, offset + 1, false);
             const_cast<AssetManager*>(this)->mAssetPaths.add(oap);
             const_cast<AssetManager*>(this)->mZipSet.addOverlay(targetPackagePath, oap);
         }
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index a4b78a6..2e3abb5 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -17,7 +17,9 @@
 #define LOG_TAG "ResourceType"
 //#define LOG_NDEBUG 0
 
+#include <androidfw/ByteBucketArray.h>
 #include <androidfw/ResourceTypes.h>
+#include <androidfw/TypeWrappers.h>
 #include <utils/Atomic.h>
 #include <utils/ByteOrder.h>
 #include <utils/Debug.h>
@@ -30,6 +32,7 @@
 #include <memory.h>
 #include <ctype.h>
 #include <stdint.h>
+#include <stddef.h>
 
 #ifndef INT32_MAX
 #define INT32_MAX ((int32_t)(2147483647))
@@ -42,7 +45,7 @@
 #define TABLE_SUPER_NOISY(x) //x
 #define LOAD_TABLE_NOISY(x) //x
 #define TABLE_THEME(x) //x
-#define LIB_NOISY(x) x
+#define LIB_NOISY(x) //x
 
 namespace android {
 
@@ -63,9 +66,8 @@
 #endif
 #endif
 
-#define IDMAP_MAGIC         0x706d6469
-// size measured in sizeof(uint32_t)
-#define IDMAP_HEADER_SIZE (ResTable::IDMAP_HEADER_SIZE_BYTES / sizeof(uint32_t))
+#define IDMAP_MAGIC             0x504D4449
+#define IDMAP_CURRENT_VERSION   0x00000001
 
 #define APP_PACKAGE_ID      0x7f
 #define SYS_PACKAGE_ID      0x01
@@ -77,6 +79,11 @@
     return (c < 0x0080 && isspace(c));
 }
 
+template<typename T>
+inline static T max(T a, T b) {
+    return a > b ? a : b;
+}
+
 // range checked; guaranteed to NUL-terminate within the stated number of available slots
 // NOTE: if this truncates the dst string due to running out of space, no attempt is
 // made to avoid splitting surrogate pairs.
@@ -215,104 +222,179 @@
     fill9patchOffsets(reinterpret_cast<Res_png_9patch*>(outData));
 }
 
-static bool assertIdmapHeader(const uint32_t* map, size_t sizeBytes)
-{
-    if (sizeBytes < ResTable::IDMAP_HEADER_SIZE_BYTES) {
-        ALOGW("idmap assertion failed: size=%d bytes\n", (int)sizeBytes);
+static bool assertIdmapHeader(const void* idmap, size_t size) {
+    if (reinterpret_cast<uintptr_t>(idmap) & 0x03) {
+        ALOGE("idmap: header is not word aligned");
         return false;
     }
-    if (*map != htodl(IDMAP_MAGIC)) { // htodl: map data expected to be in correct endianess
-        ALOGW("idmap assertion failed: invalid magic found (is 0x%08x, expected 0x%08x)\n",
-             *map, htodl(IDMAP_MAGIC));
+
+    if (size < ResTable::IDMAP_HEADER_SIZE_BYTES) {
+        ALOGW("idmap: header too small (%d bytes)", (uint32_t) size);
+        return false;
+    }
+
+    const uint32_t magic = htodl(*reinterpret_cast<const uint32_t*>(idmap));
+    if (magic != IDMAP_MAGIC) {
+        ALOGW("idmap: no magic found in header (is 0x%08x, expected 0x%08x)",
+             magic, IDMAP_MAGIC);
+        return false;
+    }
+
+    const uint32_t version = htodl(*(reinterpret_cast<const uint32_t*>(idmap) + 1));
+    if (version != IDMAP_CURRENT_VERSION) {
+        // We are strict about versions because files with this format are
+        // auto-generated and don't need backwards compatibility.
+        ALOGW("idmap: version mismatch in header (is 0x%08x, expected 0x%08x)",
+                version, IDMAP_CURRENT_VERSION);
         return false;
     }
     return true;
 }
 
-static status_t idmapLookup(const uint32_t* map, size_t sizeBytes, uint32_t key, uint32_t* outValue)
-{
-    // see README for details on the format of map
-    if (!assertIdmapHeader(map, sizeBytes)) {
-        return UNKNOWN_ERROR;
-    }
-    map = map + IDMAP_HEADER_SIZE; // skip ahead to data segment
-    // size of data block, in uint32_t
-    const size_t size = (sizeBytes - ResTable::IDMAP_HEADER_SIZE_BYTES) / sizeof(uint32_t);
-    const uint32_t type = Res_GETTYPE(key) + 1; // add one, idmap stores "public" type id
-    const uint32_t entry = Res_GETENTRY(key);
-    const uint32_t typeCount = *map;
+class IdmapEntries {
+public:
+    IdmapEntries() : mData(NULL) {}
 
-    if (type > typeCount) {
-        ALOGW("Resource ID map: type=%d exceeds number of types=%d\n", type, typeCount);
-        return UNKNOWN_ERROR;
-    }
-    if (typeCount > size) {
-        ALOGW("Resource ID map: number of types=%d exceeds size of map=%d\n", typeCount, (int)size);
-        return UNKNOWN_ERROR;
-    }
-    const uint32_t typeOffset = map[type];
-    if (typeOffset == 0) {
-        *outValue = 0;
-        return NO_ERROR;
-    }
-    if (typeOffset + 1 > size) {
-        ALOGW("Resource ID map: type offset=%d exceeds reasonable value, size of map=%d\n",
-             typeOffset, (int)size);
-        return UNKNOWN_ERROR;
-    }
-    const uint32_t entryCount = map[typeOffset];
-    const uint32_t entryOffset = map[typeOffset + 1];
-    if (entryCount == 0 || entry < entryOffset || entry - entryOffset > entryCount - 1) {
-        *outValue = 0;
-        return NO_ERROR;
-    }
-    const uint32_t index = typeOffset + 2 + entry - entryOffset;
-    if (index > size) {
-        ALOGW("Resource ID map: entry index=%u exceeds size of map=%d\n", index, (int)size);
-        *outValue = 0;
-        return NO_ERROR;
-    }
-    *outValue = map[index];
+    bool hasEntries() const {
+        if (mData == NULL) {
+            return false;
+        }
 
-    return NO_ERROR;
-}
+        return (dtohs(*mData) > 0);
+    }
 
-static status_t getIdmapPackageId(const uint32_t* map, size_t mapSize, uint32_t *outId)
-{
-    if (!assertIdmapHeader(map, mapSize)) {
-        return UNKNOWN_ERROR;
+    size_t byteSize() const {
+        if (mData == NULL) {
+            return 0;
+        }
+        uint16_t entryCount = dtohs(mData[2]);
+        return (sizeof(uint16_t) * 4) + (sizeof(uint32_t) * static_cast<size_t>(entryCount));
     }
-    if (mapSize <= IDMAP_HEADER_SIZE + 1) {
-        ALOGW("corrupt idmap: map size %d too short\n", (int)mapSize);
-        return UNKNOWN_ERROR;
+
+    uint8_t targetTypeId() const {
+        if (mData == NULL) {
+            return 0;
+        }
+        return dtohs(mData[0]);
     }
-    uint32_t typeCount = *(map + IDMAP_HEADER_SIZE);
-    if (typeCount == 0) {
-        ALOGW("corrupt idmap: no types\n");
-        return UNKNOWN_ERROR;
+
+    uint8_t overlayTypeId() const {
+        if (mData == NULL) {
+            return 0;
+        }
+        return dtohs(mData[1]);
     }
-    if (IDMAP_HEADER_SIZE + 1 + typeCount > mapSize) {
-        ALOGW("corrupt idmap: number of types %u extends past idmap size %d\n", typeCount, (int)mapSize);
-        return UNKNOWN_ERROR;
-    }
-    const uint32_t* p = map + IDMAP_HEADER_SIZE + 1;
-    // find first defined type
-    while (*p == 0) {
-        ++p;
-        if (--typeCount == 0) {
-            ALOGW("corrupt idmap: types declared, none found\n");
+
+    status_t setTo(const void* entryHeader, size_t size) {
+        if (reinterpret_cast<uintptr_t>(entryHeader) & 0x03) {
+            ALOGE("idmap: entry header is not word aligned");
             return UNKNOWN_ERROR;
         }
+
+        if (size < sizeof(uint16_t) * 4) {
+            ALOGE("idmap: entry header is too small (%u bytes)", (uint32_t) size);
+            return UNKNOWN_ERROR;
+        }
+
+        const uint16_t* header = reinterpret_cast<const uint16_t*>(entryHeader);
+        const uint16_t targetTypeId = dtohs(header[0]);
+        const uint16_t overlayTypeId = dtohs(header[1]);
+        if (targetTypeId == 0 || overlayTypeId == 0 || targetTypeId > 255 || overlayTypeId > 255) {
+            ALOGE("idmap: invalid type map (%u -> %u)", targetTypeId, overlayTypeId);
+            return UNKNOWN_ERROR;
+        }
+
+        uint16_t entryCount = dtohs(header[2]);
+        if (size < sizeof(uint32_t) * (entryCount + 2)) {
+            ALOGE("idmap: too small (%u bytes) for the number of entries (%u)",
+                    (uint32_t) size, (uint32_t) entryCount);
+            return UNKNOWN_ERROR;
+        }
+        mData = header;
+        return NO_ERROR;
     }
 
-    // determine package id from first entry of first type
-    const uint32_t offset = *p + IDMAP_HEADER_SIZE + 2;
-    if (offset > mapSize) {
-        ALOGW("corrupt idmap: entry offset %u points outside map size %d\n", offset, (int)mapSize);
+    status_t lookup(uint16_t entryId, uint16_t* outEntryId) const {
+        uint16_t entryCount = dtohs(mData[2]);
+        uint16_t offset = dtohs(mData[3]);
+
+        if (entryId < offset) {
+            // The entry is not present in this idmap
+            return BAD_INDEX;
+        }
+
+        entryId -= offset;
+
+        if (entryId >= entryCount) {
+            // The entry is not present in this idmap
+            return BAD_INDEX;
+        }
+
+        // It is safe to access the type here without checking the size because
+        // we have checked this when it was first loaded.
+        const uint32_t* entries = reinterpret_cast<const uint32_t*>(mData) + 2;
+        uint32_t mappedEntry = dtohl(entries[entryId]);
+        if (mappedEntry == 0xffffffff) {
+            // This entry is not present in this idmap
+            return BAD_INDEX;
+        }
+        *outEntryId = static_cast<uint16_t>(mappedEntry);
+        return NO_ERROR;
+    }
+
+private:
+    const uint16_t* mData;
+};
+
+status_t parseIdmap(const void* idmap, size_t size, uint8_t* outPackageId, KeyedVector<uint8_t, IdmapEntries>* outMap) {
+    if (!assertIdmapHeader(idmap, size)) {
         return UNKNOWN_ERROR;
     }
-    *outId = (map[offset] >> 24) & 0x000000ff;
 
+    size -= ResTable::IDMAP_HEADER_SIZE_BYTES;
+    if (size < sizeof(uint16_t) * 2) {
+        ALOGE("idmap: too small to contain any mapping");
+        return UNKNOWN_ERROR;
+    }
+
+    const uint16_t* data = reinterpret_cast<const uint16_t*>(
+            reinterpret_cast<const uint8_t*>(idmap) + ResTable::IDMAP_HEADER_SIZE_BYTES);
+
+    uint16_t targetPackageId = dtohs(*(data++));
+    if (targetPackageId == 0 || targetPackageId > 255) {
+        ALOGE("idmap: target package ID is invalid (%02x)", targetPackageId);
+        return UNKNOWN_ERROR;
+    }
+
+    uint16_t mapCount = dtohs(*(data++));
+    if (mapCount == 0) {
+        ALOGE("idmap: no mappings");
+        return UNKNOWN_ERROR;
+    }
+
+    if (mapCount > 255) {
+        ALOGW("idmap: too many mappings. Only 255 are possible but %u are present", (uint32_t) mapCount);
+    }
+
+    while (size > sizeof(uint16_t) * 4) {
+        IdmapEntries entries;
+        status_t err = entries.setTo(data, size);
+        if (err != NO_ERROR) {
+            return err;
+        }
+
+        ssize_t index = outMap->add(entries.overlayTypeId(), entries);
+        if (index < 0) {
+            return NO_MEMORY;
+        }
+
+        data += entries.byteSize() / sizeof(uint16_t);
+        size -= entries.byteSize();
+    }
+
+    if (outPackageId != NULL) {
+        *outPackageId = static_cast<uint8_t>(targetPackageId);
+    }
     return NO_ERROR;
 }
 
@@ -2726,7 +2808,7 @@
         free(resourceIDMap);
     }
 
-    ResTable* const                 owner;
+    const ResTable* const           owner;
     void*                           ownedData;
     const ResTable_header*          header;
     size_t                          size;
@@ -2739,6 +2821,17 @@
     size_t                          resourceIDMapSize;
 };
 
+struct ResTable::Entry {
+    ResTable_config config;
+    const ResTable_entry* entry;
+    const ResTable_type* type;
+    uint32_t specFlags;
+    const Package* package;
+
+    StringPoolRef typeStr;
+    StringPoolRef keyStr;
+};
+
 struct ResTable::Type
 {
     Type(const Header* _header, const Package* _package, size_t count)
@@ -2749,33 +2842,29 @@
     const size_t                    entryCount;
     const ResTable_typeSpec*        typeSpec;
     const uint32_t*                 typeSpecFlags;
+    IdmapEntries                    idmapEntries;
     Vector<const ResTable_type*>    configs;
 };
 
 struct ResTable::Package
 {
     Package(ResTable* _owner, const Header* _header, const ResTable_package* _package)
-        : owner(_owner), header(_header), package(_package) { }
-    ~Package()
-    {
-        size_t i = types.size();
-        while (i > 0) {
-            i--;
-            delete types[i];
+        : owner(_owner), header(_header), package(_package), typeIdOffset(0) {
+        if (dtohs(package->header.headerSize) == sizeof(package)) {
+            // The package structure is the same size as the definition.
+            // This means it contains the typeIdOffset field.
+            typeIdOffset = package->typeIdOffset;
         }
     }
 
-    ResTable* const                 owner;
+    const ResTable* const           owner;
     const Header* const             header;
     const ResTable_package* const   package;
-    Vector<Type*>                   types;
 
     ResStringPool                   typeStrings;
     ResStringPool                   keyStrings;
 
-    const Type* getType(size_t idx) const {
-        return idx < types.size() ? types[idx] : NULL;
-    }
+    size_t                          typeIdOffset;
 };
 
 // A group of objects describing a particular resource package.
@@ -2787,13 +2876,24 @@
         : owner(_owner)
         , name(_name)
         , id(_id)
-        , typeCount(0)
+        , largestTypeId(0)
         , bags(NULL)
         , dynamicRefTable(static_cast<uint8_t>(_id))
     { }
 
     ~PackageGroup() {
         clearBagCache();
+        const size_t numTypes = types.size();
+        for (size_t i = 0; i < numTypes; i++) {
+            const TypeList& typeList = types[i];
+            const size_t numInnerTypes = typeList.size();
+            for (size_t j = 0; j < numInnerTypes; j++) {
+                if (typeList[j]->package->owner == owner) {
+                    delete typeList[j];
+                }
+            }
+        }
+
         const size_t N = packages.size();
         for (size_t i=0; i<N; i++) {
             Package* pkg = packages[i];
@@ -2806,17 +2906,15 @@
     void clearBagCache() {
         if (bags) {
             TABLE_NOISY(printf("bags=%p\n", bags));
-            Package* pkg = packages[0];
-            TABLE_NOISY(printf("typeCount=%x\n", typeCount));
-            for (size_t i=0; i<typeCount; i++) {
+            for (size_t i = 0; i < bags->size(); i++) {
                 TABLE_NOISY(printf("type=%d\n", i));
-                const Type* type = pkg->getType(i);
-                if (type != NULL) {
-                    bag_set** typeBags = bags[i];
+                const TypeList& typeList = types[i];
+                if (typeList.isEmpty()) {
+                    bag_set** typeBags = bags->get(i);
                     TABLE_NOISY(printf("typeBags=%p\n", typeBags));
                     if (typeBags) {
-                        TABLE_NOISY(printf("type->entryCount=%x\n", type->entryCount));
-                        const size_t N = type->entryCount;
+                        const size_t N = typeList[0]->entryCount;
+                        TABLE_NOISY(printf("type->entryCount=%x\n", N));
                         for (size_t j=0; j<N; j++) {
                             if (typeBags[j] && typeBags[j] != (bag_set*)0xFFFFFFFF)
                                 free(typeBags[j]);
@@ -2825,25 +2923,38 @@
                     }
                 }
             }
-            free(bags);
+            delete bags;
             bags = NULL;
         }
     }
 
-    ResTable* const                 owner;
+    ssize_t findType16(const char16_t* type, size_t len) const {
+        const size_t N = packages.size();
+        for (size_t i = 0; i < N; i++) {
+            ssize_t index = packages[i]->typeStrings.indexOfString(type, len);
+            if (index >= 0) {
+                return index + packages[i]->typeIdOffset;
+            }
+        }
+        return -1;
+    }
+
+    const ResTable* const           owner;
     String16 const                  name;
     uint32_t const                  id;
+
+    // This is mainly used to keep track of the loaded packages
+    // and to clean them up properly. Accessing resources happens from
+    // the 'types' array.
     Vector<Package*>                packages;
 
-    // This is for finding typeStrings and other common package stuff.
-    Package*                        basePackage;
+    ByteBucketArray<TypeList>       types;
 
-    // For quick access.
-    size_t                          typeCount;
+    uint8_t                         largestTypeId;
 
     // Computed attribute bags, first indexed by the type and second
     // by the entry in that type.
-    bag_set***                      bags;
+    ByteBucketArray<bag_set**>*     bags;
 
     // The table mapping dynamic references to resolved references for
     // this package group.
@@ -2879,7 +2990,7 @@
 
 void ResTable::Theme::free_package(package_info* pi)
 {
-    for (size_t j=0; j<pi->numTypes; j++) {
+    for (size_t j = 0; j <= Res_MAXTYPE; j++) {
         theme_entry* te = pi->types[j].entries;
         if (te != NULL) {
             free(te);
@@ -2890,10 +3001,8 @@
 
 ResTable::Theme::package_info* ResTable::Theme::copy_package(package_info* pi)
 {
-    package_info* newpi = (package_info*)malloc(
-        sizeof(package_info) + (pi->numTypes*sizeof(type_info)));
-    newpi->numTypes = pi->numTypes;
-    for (size_t j=0; j<newpi->numTypes; j++) {
+    package_info* newpi = (package_info*)malloc(sizeof(package_info));
+    for (size_t j = 0; j <= Res_MAXTYPE; j++) {
         size_t cnt = pi->types[j].numEntries;
         newpi->types[j].numEntries = cnt;
         theme_entry* te = pi->types[j].entries;
@@ -2946,17 +3055,14 @@
             curPI = mPackages[pidx];
             if (curPI == NULL) {
                 PackageGroup* const grp = mTable.mPackageGroups[pidx];
-                int cnt = grp->typeCount;
-                curPI = (package_info*)malloc(
-                    sizeof(package_info) + (cnt*sizeof(type_info)));
-                curPI->numTypes = cnt;
-                memset(curPI->types, 0, cnt*sizeof(type_info));
+                curPI = (package_info*)malloc(sizeof(package_info));
+                memset(curPI, 0, sizeof(*curPI));
                 mPackages[pidx] = curPI;
             }
             curType = 0xffffffff;
         }
         if (curType != t) {
-            if (t >= curPI->numTypes) {
+            if (t > Res_MAXTYPE) {
                 ALOGE("Style contains key with bad type: 0x%08x\n", attrRes);
                 bag++;
                 continue;
@@ -2965,8 +3071,8 @@
             curEntries = curPI->types[t].entries;
             if (curEntries == NULL) {
                 PackageGroup* const grp = mTable.mPackageGroups[curPackageIndex];
-                const Type* type = grp->packages[0]->getType(t);
-                int cnt = type != NULL ? type->entryCount : 0;
+                const TypeList& typeList = grp->types[t];
+                int cnt = typeList.isEmpty() ? 0 : typeList[0]->entryCount;
                 curEntries = (theme_entry*)malloc(cnt*sizeof(theme_entry));
                 memset(curEntries, Res_value::TYPE_NULL, cnt*sizeof(theme_entry));
                 curPI->types[t].numEntries = cnt;
@@ -2981,8 +3087,8 @@
         }
         theme_entry* curEntry = curEntries + e;
         TABLE_NOISY(ALOGV("Attr 0x%08x: type=0x%x, data=0x%08x; curType=0x%x",
-                   attrRes, bag->map.value.dataType, bag->map.value.data,
-             curEntry->value.dataType));
+                attrRes, bag->map.value.dataType, bag->map.value.data,
+                curEntry->value.dataType));
         if (force || curEntry->value.dataType == Res_value::TYPE_NULL) {
             curEntry->stringBlock = bag->stringBlock;
             curEntry->typeSpecFlags |= bagTypeSpecFlags;
@@ -3057,8 +3163,8 @@
             const package_info* const pi = mPackages[p];
             TABLE_THEME(ALOGI("Found package: %p", pi));
             if (pi != NULL) {
-                TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, pi->numTypes));
-                if (t < pi->numTypes) {
+                TABLE_THEME(ALOGI("Desired type index is %ld in avail %d", t, Res_MAXTYPE + 1));
+                if (t <= Res_MAXTYPE) {
                     const type_info& ti = pi->types[t];
                     TABLE_THEME(ALOGI("Desired entry index is %ld in avail %d", e, ti.numEntries));
                     if (e < ti.numEntries) {
@@ -3120,14 +3226,13 @@
         package_info* pi = mPackages[i];
         if (pi == NULL) continue;
 
-        ALOGI("  Package #0x%02x:\n", (int)(i+1));
-        for (size_t j=0; j<pi->numTypes; j++) {
+        ALOGI("  Package #0x%02x:\n", (int)(i + 1));
+        for (size_t j = 0; j <= Res_MAXTYPE; j++) {
             type_info& ti = pi->types[j];
             if (ti.numEntries == 0) continue;
-
-            ALOGI("    Type #0x%02x:\n", (int)(j+1));
-            for (size_t k=0; k<ti.numEntries; k++) {
-                theme_entry& te = ti.entries[k];
+            ALOGI("    Type #0x%02x:\n", (int)(j + 1));
+            for (size_t k = 0; k < ti.numEntries; k++) {
+                const theme_entry& te = ti.entries[k];
                 if (te.value.dataType == Res_value::TYPE_NULL) continue;
                 ALOGI("      0x%08x: t=0x%x, d=0x%08x (block=%d)\n",
                      (int)Res_MAKEID(i, j, k),
@@ -3150,7 +3255,7 @@
 {
     memset(&mParams, 0, sizeof(mParams));
     memset(mPackageMap, 0, sizeof(mPackageMap));
-    addInternal(data, size, cookie, copyData, NULL /* idMap */);
+    addInternal(data, size, NULL, 0, cookie, copyData);
     LOG_FATAL_IF(mError != NO_ERROR, "Error parsing resource table");
     //ALOGI("Creating ResTable %p\n", this);
 }
@@ -3166,21 +3271,45 @@
     return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
 }
 
-status_t ResTable::add(const void* data, size_t size) {
-    return addInternal(data, size, 0 /* cookie */,
-            false /* copyData */, NULL /* idMap */);
+status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
+    return addInternal(data, size, NULL, 0, cookie, copyData);
 }
 
-status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData, const void* idmap)
-{
+status_t ResTable::add(const void* data, size_t size, const void* idmapData, size_t idmapDataSize,
+        const int32_t cookie, bool copyData) {
+    return addInternal(data, size, idmapData, idmapDataSize, cookie, copyData);
+}
+
+status_t ResTable::add(Asset* asset, const int32_t cookie, bool copyData) {
     const void* data = asset->getBuffer(true);
     if (data == NULL) {
         ALOGW("Unable to get buffer of resource asset file");
         return UNKNOWN_ERROR;
     }
-    size_t size = (size_t)asset->getLength();
-    return addInternal(data, size, cookie, copyData,
-            reinterpret_cast<const Asset*>(idmap));
+
+    return addInternal(data, static_cast<size_t>(asset->getLength()), NULL, 0, cookie, copyData);
+}
+
+status_t ResTable::add(Asset* asset, Asset* idmapAsset, const int32_t cookie, bool copyData) {
+    const void* data = asset->getBuffer(true);
+    if (data == NULL) {
+        ALOGW("Unable to get buffer of resource asset file");
+        return UNKNOWN_ERROR;
+    }
+
+    size_t idmapSize = 0;
+    const void* idmapData = NULL;
+    if (idmapAsset != NULL) {
+        idmapData = idmapAsset->getBuffer(true);
+        if (idmapData == NULL) {
+            ALOGW("Unable to get buffer of idmap asset file");
+            return UNKNOWN_ERROR;
+        }
+        idmapSize = static_cast<size_t>(idmapAsset->getLength());
+    }
+
+    return addInternal(data, static_cast<size_t>(asset->getLength()),
+            idmapData, idmapSize, cookie, copyData);
 }
 
 status_t ResTable::add(ResTable* src)
@@ -3197,8 +3326,16 @@
         for (size_t j=0; j<srcPg->packages.size(); j++) {
             pg->packages.add(srcPg->packages[j]);
         }
-        pg->basePackage = srcPg->basePackage;
-        pg->typeCount = srcPg->typeCount;
+
+        for (size_t j = 0; j < srcPg->types.size(); j++) {
+            if (srcPg->types[j].isEmpty()) {
+                continue;
+            }
+
+            TypeList& typeList = pg->types.editItemAt(j);
+            typeList.appendVector(srcPg->types[j]);
+        }
+        pg->largestTypeId = max(pg->largestTypeId, srcPg->largestTypeId);
         mPackageGroups.add(pg);
     }
 
@@ -3224,38 +3361,39 @@
     return (mError=NO_ERROR);
 }
 
-status_t ResTable::addInternal(const void* data, size_t size, const int32_t cookie,
-                       bool copyData, const Asset* idmap)
+status_t ResTable::addInternal(const void* data, size_t dataSize, const void* idmapData, size_t idmapDataSize,
+        const int32_t cookie, bool copyData)
 {
-    if (!data) return NO_ERROR;
+    if (!data) {
+        return NO_ERROR;
+    }
+
     Header* header = new Header(this);
     header->index = mHeaders.size();
     header->cookie = cookie;
-    if (idmap != NULL) {
-        const size_t idmap_size = idmap->getLength();
-        const void* idmap_data = const_cast<Asset*>(idmap)->getBuffer(true);
-        header->resourceIDMap = (uint32_t*)malloc(idmap_size);
+    if (idmapData != NULL) {
+        header->resourceIDMap = (uint32_t*) malloc(idmapDataSize);
         if (header->resourceIDMap == NULL) {
             delete header;
             return (mError = NO_MEMORY);
         }
-        memcpy((void*)header->resourceIDMap, idmap_data, idmap_size);
-        header->resourceIDMapSize = idmap_size;
+        memcpy(header->resourceIDMap, idmapData, idmapDataSize);
+        header->resourceIDMapSize = idmapDataSize;
     }
     mHeaders.add(header);
 
     const bool notDeviceEndian = htods(0xf0) != 0xf0;
 
     LOAD_TABLE_NOISY(
-        ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, asset=%p, copy=%d "
-             "idmap=%p\n", data, size, cookie, asset, copyData, idmap));
+        ALOGV("Adding resources to ResTable: data=%p, size=0x%x, cookie=%d, copy=%d "
+             "idmap=%p\n", data, dataSize, cookie, copyData, idmap));
 
     if (copyData || notDeviceEndian) {
-        header->ownedData = malloc(size);
+        header->ownedData = malloc(dataSize);
         if (header->ownedData == NULL) {
             return (mError=NO_MEMORY);
         }
-        memcpy(header->ownedData, data, size);
+        memcpy(header->ownedData, data, dataSize);
         data = header->ownedData;
     }
 
@@ -3265,10 +3403,10 @@
     //     dtohl(header->header->header.size), header->header->header.size);
     LOAD_TABLE_NOISY(ALOGV("Loading ResTable @%p:\n", header->header));
     if (dtohs(header->header->header.headerSize) > header->size
-            || header->size > size) {
+            || header->size > dataSize) {
         ALOGW("Bad resource table: header size 0x%x or total size 0x%x is larger than data size 0x%x\n",
              (int)dtohs(header->header->header.headerSize),
-             (int)header->size, (int)size);
+             (int)header->size, (int)dataSize);
         return (mError=BAD_TYPE);
     }
     if (((dtohs(header->header->header.headerSize)|header->size)&0x3) != 0) {
@@ -3313,16 +3451,8 @@
                      dtohl(header->header->packageCount));
                 return (mError=BAD_TYPE);
             }
-            uint32_t idmap_id = 0;
-            if (idmap != NULL) {
-                uint32_t tmp;
-                if (getIdmapPackageId(header->resourceIDMap,
-                                      header->resourceIDMapSize,
-                                      &tmp) == NO_ERROR) {
-                    idmap_id = tmp;
-                }
-            }
-            if (parsePackage((ResTable_package*)chunk, header, idmap_id) != NO_ERROR) {
+
+            if (parsePackage((ResTable_package*)chunk, header) != NO_ERROR) {
                 return mError;
             }
             curPackage++;
@@ -3405,46 +3535,38 @@
         ALOGW("Bad identifier when getting name for resource number 0x%08x", resID);
         return false;
     }
-    if (grp->packages.size() > 0) {
-        const Package* const package = grp->packages[0];
 
-        const ResTable_type* type;
-        const ResTable_entry* entry;
-        ssize_t offset = getEntry(package, t, e, NULL, &type, &entry, NULL);
-        if (offset <= 0) {
-            return false;
-        }
-
-        outName->package = grp->name.string();
-        outName->packageLen = grp->name.size();
-        if (allowUtf8) {
-            outName->type8 = grp->basePackage->typeStrings.string8At(t, &outName->typeLen);
-            outName->name8 = grp->basePackage->keyStrings.string8At(
-                dtohl(entry->key.index), &outName->nameLen);
-        } else {
-            outName->type8 = NULL;
-            outName->name8 = NULL;
-        }
-        if (outName->type8 == NULL) {
-            outName->type = grp->basePackage->typeStrings.stringAt(t, &outName->typeLen);
-            // If we have a bad index for some reason, we should abort.
-            if (outName->type == NULL) {
-                return false;
-            }
-        }
-        if (outName->name8 == NULL) {
-            outName->name = grp->basePackage->keyStrings.stringAt(
-                dtohl(entry->key.index), &outName->nameLen);
-            // If we have a bad index for some reason, we should abort.
-            if (outName->name == NULL) {
-                return false;
-            }
-        }
-
-        return true;
+    Entry entry;
+    status_t err = getEntry(grp, t, e, NULL, &entry);
+    if (err != NO_ERROR) {
+        return false;
     }
 
-    return false;
+    outName->package = grp->name.string();
+    outName->packageLen = grp->name.size();
+    if (allowUtf8) {
+        outName->type8 = entry.typeStr.string8(&outName->typeLen);
+        outName->name8 = entry.keyStr.string8(&outName->nameLen);
+    } else {
+        outName->type8 = NULL;
+        outName->name8 = NULL;
+    }
+    if (outName->type8 == NULL) {
+        outName->type = entry.typeStr.string16(&outName->typeLen);
+        // If we have a bad index for some reason, we should abort.
+        if (outName->type == NULL) {
+            return false;
+        }
+    }
+    if (outName->name8 == NULL) {
+        outName->name = entry.keyStr.string16(&outName->nameLen);
+        // If we have a bad index for some reason, we should abort.
+        if (outName->name == NULL) {
+            return false;
+        }
+    }
+
+    return true;
 }
 
 ssize_t ResTable::getResource(uint32_t resID, Res_value* outValue, bool mayBeBag, uint16_t density,
@@ -3471,15 +3593,6 @@
         return BAD_INDEX;
     }
 
-    const Res_value* bestValue = NULL;
-    const Package* bestPackage = NULL;
-    ResTable_config bestItem;
-    memset(&bestItem, 0, sizeof(bestItem)); // make the compiler shut up
-
-    if (outSpecFlags != NULL) *outSpecFlags = 0;
-
-    // Look through all resource packages, starting with the most
-    // recently added.
     const PackageGroup* const grp = mPackageGroups[p];
     if (grp == NULL) {
         ALOGW("Bad identifier when getting value for resource number 0x%08x", resID);
@@ -3487,142 +3600,62 @@
     }
 
     // Allow overriding density
-    const ResTable_config* desiredConfig = &mParams;
-    ResTable_config* overrideConfig = NULL;
+    ResTable_config desiredConfig = mParams;
     if (density > 0) {
-        overrideConfig = (ResTable_config*) malloc(sizeof(ResTable_config));
-        if (overrideConfig == NULL) {
-            ALOGE("Couldn't malloc ResTable_config for overrides: %s", strerror(errno));
-            return BAD_INDEX;
-        }
-        memcpy(overrideConfig, &mParams, sizeof(ResTable_config));
-        overrideConfig->density = density;
-        desiredConfig = overrideConfig;
+        desiredConfig.density = density;
     }
 
-    ssize_t rc = BAD_VALUE;
-    size_t ip = grp->packages.size();
-    while (ip > 0) {
-        ip--;
-        int T = t;
-        int E = e;
-
-        const Package* const package = grp->packages[ip];
-        if (package->header->resourceIDMap) {
-            uint32_t overlayResID = 0x0;
-            status_t retval = idmapLookup(package->header->resourceIDMap,
-                                          package->header->resourceIDMapSize,
-                                          resID, &overlayResID);
-            if (retval == NO_ERROR && overlayResID != 0x0) {
-                // for this loop iteration, this is the type and entry we really want
-                ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
-                T = Res_GETTYPE(overlayResID);
-                E = Res_GETENTRY(overlayResID);
-            } else {
-                // resource not present in overlay package, continue with the next package
-                continue;
-            }
-        }
-
-        const ResTable_type* type;
-        const ResTable_entry* entry;
-        const Type* typeClass;
-        ssize_t offset = getEntry(package, T, E, desiredConfig, &type, &entry, &typeClass);
-        if (offset <= 0) {
-            // No {entry, appropriate config} pair found in package. If this
-            // package is an overlay package (ip != 0), this simply means the
-            // overlay package did not specify a default.
-            // Non-overlay packages are still required to provide a default.
-            if (offset < 0 && ip == 0) {
-                ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) in package %zd (error %d)\n",
-                        resID, T, E, ip, (int)offset);
-                rc = offset;
-                goto out;
-            }
-            continue;
-        }
-
-        if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) != 0) {
-            if (!mayBeBag) {
-                ALOGW("Requesting resource 0x%x failed because it is complex\n",
-                     resID);
-            }
-            continue;
-        }
-
-        if ((size_t)offset > (dtohl(type->header.size)-sizeof(Res_value))) {
-            ALOGW("ResTable_item at %d is beyond type chunk data %d",
-                 (int)offset, dtohl(type->header.size));
-            rc = BAD_TYPE;
-            goto out;
-        }
-
-        const Res_value* item =
-            (const Res_value*)(((const uint8_t*)type) + offset);
-        ResTable_config thisConfig;
-        thisConfig.copyFromDtoH(type->config);
-
-        if (outSpecFlags != NULL) {
-            if (typeClass->typeSpecFlags != NULL) {
-                *outSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
-            } else {
-                *outSpecFlags = -1;
-            }
-        }
-
-        if (bestPackage != NULL &&
-            (bestItem.isMoreSpecificThan(thisConfig) || bestItem.diff(thisConfig) == 0)) {
-            // Discard thisConfig not only if bestItem is more specific, but also if the two configs
-            // are identical (diff == 0), or overlay packages will not take effect.
-            continue;
-        }
-
-        bestItem = thisConfig;
-        bestValue = item;
-        bestPackage = package;
+    Entry entry;
+    status_t err = getEntry(grp, t, e, &desiredConfig, &entry);
+    if (err != NO_ERROR) {
+        ALOGW("Failure getting entry for 0x%08x (t=%d e=%d) (error %d)\n",
+                resID, t, e, err);
+        return err;
     }
 
-    TABLE_NOISY(printf("Found result: package %p\n", bestPackage));
-
-    if (bestValue) {
-        outValue->size = dtohs(bestValue->size);
-        outValue->res0 = bestValue->res0;
-        outValue->dataType = bestValue->dataType;
-        outValue->data = dtohl(bestValue->data);
-
-        // The reference may be pointing to a resource in a shared library. These
-        // references have build-time generated package IDs. These ids may not match
-        // the actual package IDs of the corresponding packages in this ResTable.
-        // We need to fix the package ID based on a mapping.
-        status_t err = grp->dynamicRefTable.lookupResourceValue(outValue);
-        if (err != NO_ERROR) {
-            ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data);
-            rc = BAD_VALUE;
-            goto out;
+    if ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_COMPLEX) != 0) {
+        if (!mayBeBag) {
+            ALOGW("Requesting resource 0x%08x failed because it is complex\n", resID);
         }
-
-        if (outConfig != NULL) {
-            *outConfig = bestItem;
-        }
-        TABLE_NOISY(size_t len;
-              printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n",
-                     bestPackage->header->index,
-                     outValue->dataType,
-                     outValue->dataType == bestValue->TYPE_STRING
-                     ? String8(bestPackage->header->values.stringAt(
-                         outValue->data, &len)).string()
-                     : "",
-                     outValue->data));
-        rc = bestPackage->header->index;
-        goto out;
+        return BAD_VALUE;
     }
 
-out:
-    if (overrideConfig != NULL) {
-        free(overrideConfig);
+    const Res_value* value = reinterpret_cast<const Res_value*>(
+            reinterpret_cast<const uint8_t*>(entry.entry) + entry.entry->size);
+
+    outValue->size = dtohs(value->size);
+    outValue->res0 = value->res0;
+    outValue->dataType = value->dataType;
+    outValue->data = dtohl(value->data);
+
+    // The reference may be pointing to a resource in a shared library. These
+    // references have build-time generated package IDs. These ids may not match
+    // the actual package IDs of the corresponding packages in this ResTable.
+    // We need to fix the package ID based on a mapping.
+    if (grp->dynamicRefTable.lookupResourceValue(outValue) != NO_ERROR) {
+        ALOGW("Failed to resolve referenced package: 0x%08x", outValue->data);
+        return BAD_VALUE;
     }
 
-    return rc;
+    TABLE_NOISY(size_t len;
+          printf("Found value: pkg=%d, type=%d, str=%s, int=%d\n",
+                 entry.package->header->index,
+                 outValue->dataType,
+                 outValue->dataType == Res_value::TYPE_STRING
+                 ? String8(entry.package->header->values.stringAt(
+                     outValue->data, &len)).string()
+                 : "",
+                 outValue->data));
+
+    if (outSpecFlags != NULL) {
+        *outSpecFlags = entry.specFlags;
+    }
+
+    if (outConfig != NULL) {
+        *outConfig = entry.config;
+    }
+
+    return entry.package->header->index;
 }
 
 ssize_t ResTable::resolveReference(Res_value* value, ssize_t blockIndex,
@@ -3721,29 +3754,25 @@
     PackageGroup* const grp = mPackageGroups[p];
     if (grp == NULL) {
         ALOGW("Bad identifier when getting bag for resource number 0x%08x", resID);
-        return false;
-    }
-
-    if (t >= (int)grp->typeCount) {
-        ALOGW("Type identifier 0x%x is larger than type count 0x%x",
-             t+1, (int)grp->typeCount);
         return BAD_INDEX;
     }
 
-    const Package* const basePackage = grp->packages[0];
+    const TypeList& typeConfigs = grp->types[t];
+    if (typeConfigs.isEmpty()) {
+        ALOGW("Type identifier 0x%x does not exist.", t+1);
+        return BAD_INDEX;
+    }
 
-    const Type* const typeConfigs = basePackage->getType(t);
-
-    const size_t NENTRY = typeConfigs->entryCount;
+    const size_t NENTRY = typeConfigs[0]->entryCount;
     if (e >= (int)NENTRY) {
         ALOGW("Entry identifier 0x%x is larger than entry count 0x%x",
-             e, (int)typeConfigs->entryCount);
+             e, (int)typeConfigs[0]->entryCount);
         return BAD_INDEX;
     }
 
     // First see if we've already computed this bag...
     if (grp->bags) {
-        bag_set** typeSet = grp->bags[t];
+        bag_set** typeSet = grp->bags->get(t);
         if (typeSet) {
             bag_set* set = typeSet[e];
             if (set) {
@@ -3764,229 +3793,174 @@
 
     // Bag not found, we need to compute it!
     if (!grp->bags) {
-        grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*));
+        grp->bags = new ByteBucketArray<bag_set**>();
         if (!grp->bags) return NO_MEMORY;
     }
 
-    bag_set** typeSet = grp->bags[t];
+    bag_set** typeSet = grp->bags->get(t);
     if (!typeSet) {
         typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*));
         if (!typeSet) return NO_MEMORY;
-        grp->bags[t] = typeSet;
+        grp->bags->set(t, typeSet);
     }
 
     // Mark that we are currently working on this one.
     typeSet[e] = (bag_set*)0xFFFFFFFF;
 
+    TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
+
+    // Now collect all bag attributes
+    Entry entry;
+    status_t err = getEntry(grp, t, e, &mParams, &entry);
+    if (err != NO_ERROR) {
+        return err;
+    }
+
+    const uint16_t entrySize = dtohs(entry.entry->size);
+    const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
+        ? dtohl(((const ResTable_map_entry*)entry.entry)->parent.ident) : 0;
+    const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
+        ? dtohl(((const ResTable_map_entry*)entry.entry)->count) : 0;
+
+    size_t N = count;
+
+    TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
+                     entrySize, parent, count));
+
+    // If this map inherits from another, we need to start
+    // with its parent's values.  Otherwise start out empty.
+    TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
+                       entrySize, parent));
+
     // This is what we are building.
     bag_set* set = NULL;
 
-    TABLE_NOISY(ALOGI("Building bag: %p\n", (void*)resID));
+    if (parent) {
+        uint32_t resolvedParent = parent;
 
-    ResTable_config bestConfig;
-    memset(&bestConfig, 0, sizeof(bestConfig));
-
-    // Now collect all bag attributes from all packages.
-    size_t ip = grp->packages.size();
-    while (ip > 0) {
-        ip--;
-        int T = t;
-        int E = e;
-
-        const Package* const package = grp->packages[ip];
-        if (package->header->resourceIDMap) {
-            uint32_t overlayResID = 0x0;
-            status_t retval = idmapLookup(package->header->resourceIDMap,
-                                          package->header->resourceIDMapSize,
-                                          resID, &overlayResID);
-            if (retval == NO_ERROR && overlayResID != 0x0) {
-                // for this loop iteration, this is the type and entry we really want
-                ALOGV("resource map 0x%08x -> 0x%08x\n", resID, overlayResID);
-                T = Res_GETTYPE(overlayResID);
-                E = Res_GETENTRY(overlayResID);
-            } else {
-                // resource not present in overlay package, continue with the next package
-                continue;
-            }
+        // Bags encode a parent reference without using the standard
+        // Res_value structure. That means we must always try to
+        // resolve a parent reference in case it is actually a
+        // TYPE_DYNAMIC_REFERENCE.
+        status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent);
+        if (err != NO_ERROR) {
+            ALOGE("Failed resolving bag parent id 0x%08x", parent);
+            return UNKNOWN_ERROR;
         }
 
-        const ResTable_type* type;
-        const ResTable_entry* entry;
-        const Type* typeClass;
-        ALOGV("Getting entry pkg=%p, t=%d, e=%d\n", package, T, E);
-        ssize_t offset = getEntry(package, T, E, &mParams, &type, &entry, &typeClass);
-        ALOGV("Resulting offset=%d\n", (int)offset);
-        if (offset <= 0) {
-            // No {entry, appropriate config} pair found in package. If this
-            // package is an overlay package (ip != 0), this simply means the
-            // overlay package did not specify a default.
-            // Non-overlay packages are still required to provide a default.
-            if (offset < 0 && ip == 0) {
-                if (set) free(set);
-                return offset;
-            }
-            continue;
+        const bag_entry* parentBag;
+        uint32_t parentTypeSpecFlags = 0;
+        const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags);
+        const size_t NT = ((NP >= 0) ? NP : 0) + N;
+        set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
+        if (set == NULL) {
+            return NO_MEMORY;
         }
-
-        if ((dtohs(entry->flags)&entry->FLAG_COMPLEX) == 0) {
-            ALOGW("Skipping entry 0x%x in package table %zu because it is not complex!\n",
-                 resID, ip);
-            continue;
-        }
-
-        if (set != NULL && !type->config.isBetterThan(bestConfig, NULL)) {
-            continue;
-        }
-        bestConfig = type->config;
-        if (set) {
-            free(set);
-            set = NULL;
-        }
-
-        const uint16_t entrySize = dtohs(entry->size);
-        const uint32_t parent = entrySize >= sizeof(ResTable_map_entry)
-            ? dtohl(((const ResTable_map_entry*)entry)->parent.ident) : 0;
-        const uint32_t count = entrySize >= sizeof(ResTable_map_entry)
-            ? dtohl(((const ResTable_map_entry*)entry)->count) : 0;
-
-        size_t N = count;
-
-        TABLE_NOISY(ALOGI("Found map: size=%p parent=%p count=%d\n",
-                         entrySize, parent, count));
-
-        // If this map inherits from another, we need to start
-        // with its parent's values.  Otherwise start out empty.
-        TABLE_NOISY(printf("Creating new bag, entrySize=0x%08x, parent=0x%08x\n",
-                           entrySize, parent));
-        if (parent) {
-            uint32_t resolvedParent = parent;
-
-            // Bags encode a parent reference without using the standard
-            // Res_value structure. That means we must always try to
-            // resolve a parent reference in case it is actually a
-            // TYPE_DYNAMIC_REFERENCE.
-            status_t err = grp->dynamicRefTable.lookupResourceId(&resolvedParent);
-            if (err != NO_ERROR) {
-                ALOGE("Failed resolving bag parent id 0x%08x", parent);
-                return UNKNOWN_ERROR;
-            }
-
-            const bag_entry* parentBag;
-            uint32_t parentTypeSpecFlags = 0;
-            const ssize_t NP = getBagLocked(resolvedParent, &parentBag, &parentTypeSpecFlags);
-            const size_t NT = ((NP >= 0) ? NP : 0) + N;
-            set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*NT);
-            if (set == NULL) {
-                return NO_MEMORY;
-            }
-            if (NP > 0) {
-                memcpy(set+1, parentBag, NP*sizeof(bag_entry));
-                set->numAttrs = NP;
-                TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP));
-            } else {
-                TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n"));
-                set->numAttrs = 0;
-            }
-            set->availAttrs = NT;
-            set->typeSpecFlags = parentTypeSpecFlags;
+        if (NP > 0) {
+            memcpy(set+1, parentBag, NP*sizeof(bag_entry));
+            set->numAttrs = NP;
+            TABLE_NOISY(ALOGI("Initialized new bag with %d inherited attributes.\n", NP));
         } else {
-            set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
-            if (set == NULL) {
-                return NO_MEMORY;
-            }
+            TABLE_NOISY(ALOGI("Initialized new bag with no inherited attributes.\n"));
             set->numAttrs = 0;
-            set->availAttrs = N;
-            set->typeSpecFlags = 0;
         }
-
-        if (typeClass->typeSpecFlags != NULL) {
-            set->typeSpecFlags |= dtohl(typeClass->typeSpecFlags[E]);
-        } else {
-            set->typeSpecFlags = -1;
+        set->availAttrs = NT;
+        set->typeSpecFlags = parentTypeSpecFlags;
+    } else {
+        set = (bag_set*)malloc(sizeof(bag_set)+sizeof(bag_entry)*N);
+        if (set == NULL) {
+            return NO_MEMORY;
         }
+        set->numAttrs = 0;
+        set->availAttrs = N;
+        set->typeSpecFlags = 0;
+    }
 
-        // Now merge in the new attributes...
-        ssize_t curOff = offset;
-        const ResTable_map* map;
-        bag_entry* entries = (bag_entry*)(set+1);
-        size_t curEntry = 0;
-        uint32_t pos = 0;
-        TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n",
-                     set, entries, set->availAttrs));
-        while (pos < count) {
-            TABLE_NOISY(printf("Now at %p\n", (void*)curOff));
+    set->typeSpecFlags |= entry.specFlags;
 
-            if ((size_t)curOff > (dtohl(type->header.size)-sizeof(ResTable_map))) {
-                ALOGW("ResTable_map at %d is beyond type chunk data %d",
-                     (int)curOff, dtohl(type->header.size));
-                return BAD_TYPE;
-            }
-            map = (const ResTable_map*)(((const uint8_t*)type) + curOff);
-            N++;
+    // Now merge in the new attributes...
+    size_t curOff = (reinterpret_cast<uintptr_t>(entry.entry) - reinterpret_cast<uintptr_t>(entry.type))
+        + dtohs(entry.entry->size);
+    const ResTable_map* map;
+    bag_entry* entries = (bag_entry*)(set+1);
+    size_t curEntry = 0;
+    uint32_t pos = 0;
+    TABLE_NOISY(ALOGI("Starting with set %p, entries=%p, avail=%d\n",
+                 set, entries, set->availAttrs));
+    while (pos < count) {
+        TABLE_NOISY(printf("Now at %p\n", (void*)curOff));
 
-            const uint32_t newName = htodl(map->name.ident);
-            bool isInside;
-            uint32_t oldName = 0;
-            while ((isInside=(curEntry < set->numAttrs))
-                    && (oldName=entries[curEntry].map.name.ident) < newName) {
-                TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n",
-                             curEntry, entries[curEntry].map.name.ident));
-                curEntry++;
-            }
+        if (curOff > (dtohl(entry.type->header.size)-sizeof(ResTable_map))) {
+            ALOGW("ResTable_map at %d is beyond type chunk data %d",
+                 (int)curOff, dtohl(entry.type->header.size));
+            return BAD_TYPE;
+        }
+        map = (const ResTable_map*)(((const uint8_t*)entry.type) + curOff);
+        N++;
 
-            if ((!isInside) || oldName != newName) {
-                // This is a new attribute...  figure out what to do with it.
-                if (set->numAttrs >= set->availAttrs) {
-                    // Need to alloc more memory...
-                    const size_t newAvail = set->availAttrs+N;
-                    set = (bag_set*)realloc(set,
-                                            sizeof(bag_set)
-                                            + sizeof(bag_entry)*newAvail);
-                    if (set == NULL) {
-                        return NO_MEMORY;
-                    }
-                    set->availAttrs = newAvail;
-                    entries = (bag_entry*)(set+1);
-                    TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n",
-                                 set, entries, set->availAttrs));
-                }
-                if (isInside) {
-                    // Going in the middle, need to make space.
-                    memmove(entries+curEntry+1, entries+curEntry,
-                            sizeof(bag_entry)*(set->numAttrs-curEntry));
-                    set->numAttrs++;
-                }
-                TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n",
-                             curEntry, newName));
-            } else {
-                TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n",
-                             curEntry, oldName));
-            }
-
-            bag_entry* cur = entries+curEntry;
-
-            cur->stringBlock = package->header->index;
-            cur->map.name.ident = newName;
-            cur->map.value.copyFrom_dtoh(map->value);
-            status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value);
-            if (err != NO_ERROR) {
-                ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data);
-                return UNKNOWN_ERROR;
-            }
-
-            TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n",
-                         curEntry, cur, cur->stringBlock, cur->map.name.ident,
-                         cur->map.value.dataType, cur->map.value.data));
-
-            // On to the next!
+        const uint32_t newName = htodl(map->name.ident);
+        bool isInside;
+        uint32_t oldName = 0;
+        while ((isInside=(curEntry < set->numAttrs))
+                && (oldName=entries[curEntry].map.name.ident) < newName) {
+            TABLE_NOISY(printf("#%d: Keeping existing attribute: 0x%08x\n",
+                         curEntry, entries[curEntry].map.name.ident));
             curEntry++;
-            pos++;
-            const size_t size = dtohs(map->value.size);
-            curOff += size + sizeof(*map)-sizeof(map->value);
-        };
-        if (curEntry > set->numAttrs) {
-            set->numAttrs = curEntry;
         }
+
+        if ((!isInside) || oldName != newName) {
+            // This is a new attribute...  figure out what to do with it.
+            if (set->numAttrs >= set->availAttrs) {
+                // Need to alloc more memory...
+                const size_t newAvail = set->availAttrs+N;
+                set = (bag_set*)realloc(set,
+                                        sizeof(bag_set)
+                                        + sizeof(bag_entry)*newAvail);
+                if (set == NULL) {
+                    return NO_MEMORY;
+                }
+                set->availAttrs = newAvail;
+                entries = (bag_entry*)(set+1);
+                TABLE_NOISY(printf("Reallocated set %p, entries=%p, avail=%d\n",
+                             set, entries, set->availAttrs));
+            }
+            if (isInside) {
+                // Going in the middle, need to make space.
+                memmove(entries+curEntry+1, entries+curEntry,
+                        sizeof(bag_entry)*(set->numAttrs-curEntry));
+                set->numAttrs++;
+            }
+            TABLE_NOISY(printf("#%d: Inserting new attribute: 0x%08x\n",
+                         curEntry, newName));
+        } else {
+            TABLE_NOISY(printf("#%d: Replacing existing attribute: 0x%08x\n",
+                         curEntry, oldName));
+        }
+
+        bag_entry* cur = entries+curEntry;
+
+        cur->stringBlock = entry.package->header->index;
+        cur->map.name.ident = newName;
+        cur->map.value.copyFrom_dtoh(map->value);
+        status_t err = grp->dynamicRefTable.lookupResourceValue(&cur->map.value);
+        if (err != NO_ERROR) {
+            ALOGE("Reference item(0x%08x) in bag could not be resolved.", cur->map.value.data);
+            return UNKNOWN_ERROR;
+        }
+
+        TABLE_NOISY(printf("Setting entry #%d %p: block=%d, name=0x%08x, type=%d, data=0x%08x\n",
+                     curEntry, cur, cur->stringBlock, cur->map.name.ident,
+                     cur->map.value.dataType, cur->map.value.data));
+
+        // On to the next!
+        curEntry++;
+        pos++;
+        const size_t size = dtohs(map->value.size);
+        curOff += size + sizeof(*map)-sizeof(map->value);
+    };
+
+    if (curEntry > set->numAttrs) {
+        set->numAttrs = curEntry;
     }
 
     // And this is it...
@@ -4154,80 +4128,63 @@
             continue;
         }
 
-        const ssize_t ti = group->basePackage->typeStrings.indexOfString(type, typeLen);
+        const ssize_t ti = group->findType16(type, typeLen);
         if (ti < 0) {
             TABLE_NOISY(printf("Type not found in package %s\n", String8(group->name).string()));
             continue;
         }
 
-        const ssize_t ei = group->basePackage->keyStrings.indexOfString(name, nameLen);
-        if (ei < 0) {
-            TABLE_NOISY(printf("Name not found in package %s\n", String8(group->name).string()));
+        const TypeList& typeList = group->types[ti];
+        if (typeList.isEmpty()) {
+            TABLE_NOISY(printf("Expected type structure not found in package %s for index %d\n",
+                               String8(group->name).string(), ti));
             continue;
         }
 
-        TABLE_NOISY(printf("Search indices: type=%d, name=%d\n", ti, ei));
+        const size_t typeCount = typeList.size();
+        for (size_t i = 0; i < typeCount; i++) {
+            const Type* t = typeList[i];
+            const ssize_t ei = t->package->keyStrings.indexOfString(name, nameLen);
+            if (ei < 0) {
+                continue;
+            }
 
-        const Type* const typeConfigs = group->packages[0]->getType(ti);
-        if (typeConfigs == NULL || typeConfigs->configs.size() <= 0) {
-            TABLE_NOISY(printf("Expected type structure not found in package %s for idnex %d\n",
-                               String8(group->name).string(), ti));
-        }
-
-        size_t NTC = typeConfigs->configs.size();
-        for (size_t tci=0; tci<NTC; tci++) {
-            const ResTable_type* const ty = typeConfigs->configs[tci];
-            const uint32_t typeOffset = dtohl(ty->entriesStart);
-
-            const uint8_t* const end = ((const uint8_t*)ty) + dtohl(ty->header.size);
-            const uint32_t* const eindex = (const uint32_t*)
-                (((const uint8_t*)ty) + dtohs(ty->header.headerSize));
-
-            const size_t NE = dtohl(ty->entryCount);
-            for (size_t i=0; i<NE; i++) {
-                uint32_t offset = dtohl(eindex[i]);
-                if (offset == ResTable_type::NO_ENTRY) {
-                    continue;
-                }
-
-                offset += typeOffset;
-
-                if (offset > (dtohl(ty->header.size)-sizeof(ResTable_entry))) {
-                    ALOGW("ResTable_entry at %d is beyond type chunk data %d",
-                         offset, dtohl(ty->header.size));
-                    return 0;
-                }
-                if ((offset&0x3) != 0) {
-                    ALOGW("ResTable_entry at %d (pkg=%d type=%d ent=%d) is not on an integer boundary when looking for %s:%s/%s",
-                         (int)offset, (int)group->id, (int)ti+1, (int)i,
-                         String8(package, packageLen).string(),
-                         String8(type, typeLen).string(),
-                         String8(name, nameLen).string());
-                    return 0;
-                }
-
-                const ResTable_entry* const entry = (const ResTable_entry*)
-                    (((const uint8_t*)ty) + offset);
-                if (dtohs(entry->size) < sizeof(*entry)) {
-                    ALOGW("ResTable_entry size %d is too small", dtohs(entry->size));
-                    return BAD_TYPE;
-                }
-
-                TABLE_SUPER_NOISY(printf("Looking at entry #%d: want str %d, have %d\n",
-                                         i, ei, dtohl(entry->key.index)));
-                if (dtohl(entry->key.index) == (size_t)ei) {
-                    if (outTypeSpecFlags) {
-                        *outTypeSpecFlags = typeConfigs->typeSpecFlags[i];
-                        if (fakePublic) {
-                            *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
-                        }
+            const size_t configCount = t->configs.size();
+            for (size_t j = 0; j < configCount; j++) {
+                const TypeVariant tv(t->configs[j]);
+                for (TypeVariant::iterator iter = tv.beginEntries();
+                     iter != tv.endEntries();
+                     iter++) {
+                    const ResTable_entry* entry = *iter;
+                    if (entry == NULL) {
+                        continue;
                     }
-                    return Res_MAKEID(group->id-1, ti, i);
+
+                    if (dtohl(entry->key.index) == (size_t) ei) {
+                        uint32_t resId = Res_MAKEID(group->id - 1, ti, iter.index());
+                        if (outTypeSpecFlags) {
+                            Entry result;
+                            if (getEntry(group, ti, iter.index(), NULL, &result) != NO_ERROR) {
+                                ALOGW("Failed to find spec flags for %s:%s/%s (0x%08x)",
+                                        String8(group->name).string(),
+                                        String8(String16(type, typeLen)).string(),
+                                        String8(String16(name, nameLen)).string(),
+                                        resId);
+                                return 0;
+                            }
+                            *outTypeSpecFlags = result.specFlags;
+
+                            if (fakePublic) {
+                                *outTypeSpecFlags |= ResTable_typeSpec::SPEC_PUBLIC;
+                            }
+                        }
+                        return resId;
+                    }
                 }
             }
         }
+        break;
     }
-
     return 0;
 }
 
@@ -5260,6 +5217,18 @@
     return mPackageGroups[idx]->id;
 }
 
+uint32_t ResTable::getLastTypeIdForPackage(size_t idx) const
+{
+    if (mError != NO_ERROR) {
+        return 0;
+    }
+    LOG_FATAL_IF(idx >= mPackageGroups.size(),
+            "Requested package index %d past package count %d",
+            (int)idx, (int)mPackageGroups.size());
+    const PackageGroup* const group = mPackageGroups[idx];
+    return group->largestTypeId;
+}
+
 size_t ResTable::getTableCount() const
 {
     return mHeaders.size();
@@ -5292,32 +5261,31 @@
 
 void ResTable::getConfigurations(Vector<ResTable_config>* configs) const
 {
-    const size_t I = mPackageGroups.size();
-    for (size_t i=0; i<I; i++) {
+    const size_t packageCount = mPackageGroups.size();
+    for (size_t i = 0; i < packageCount; i++) {
         const PackageGroup* packageGroup = mPackageGroups[i];
-        const size_t J = packageGroup->packages.size();
-        for (size_t j=0; j<J; j++) {
-            const Package* package = packageGroup->packages[j];
-            const size_t K = package->types.size();
-            for (size_t k=0; k<K; k++) {
-                const Type* type = package->types[k];
-                if (type == NULL) continue;
-                const size_t L = type->configs.size();
-                for (size_t l=0; l<L; l++) {
-                    const ResTable_type* config = type->configs[l];
+        const size_t typeCount = packageGroup->types.size();
+        for (size_t j = 0; j < typeCount; j++) {
+            const TypeList& typeList = packageGroup->types[j];
+            const size_t numTypes = typeList.size();
+            for (size_t k = 0; k < numTypes; k++) {
+                const Type* type = typeList[k];
+                const size_t numConfigs = type->configs.size();
+                for (size_t m = 0; m < numConfigs; m++) {
+                    const ResTable_type* config = type->configs[m];
                     ResTable_config cfg;
                     memset(&cfg, 0, sizeof(ResTable_config));
                     cfg.copyFromDtoH(config->config);
                     // only insert unique
-                    const size_t M = configs->size();
-                    size_t m;
-                    for (m=0; m<M; m++) {
-                        if (0 == (*configs)[m].compare(cfg)) {
+                    const size_t N = configs->size();
+                    size_t n;
+                    for (n = 0; n < N; n++) {
+                        if (0 == (*configs)[n].compare(cfg)) {
                             break;
                         }
                     }
                     // if we didn't find it
-                    if (m == M) {
+                    if (n == N) {
                         configs->add(cfg);
                     }
                 }
@@ -5350,122 +5318,180 @@
     }
 }
 
-ssize_t ResTable::getEntry(
-    const Package* package, int typeIndex, int entryIndex,
-    const ResTable_config* config,
-    const ResTable_type** outType, const ResTable_entry** outEntry,
-    const Type** outTypeClass) const
-{
-    ALOGV("Getting entry from package %p\n", package);
-    const ResTable_package* const pkg = package->package;
+StringPoolRef::StringPoolRef(const ResStringPool* pool, uint32_t index)
+    : mPool(pool), mIndex(index) {}
 
-    const Type* allTypes = package->getType(typeIndex);
-    ALOGV("allTypes=%p\n", allTypes);
-    if (allTypes == NULL) {
-        ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
-        return 0;
+StringPoolRef::StringPoolRef()
+    : mPool(NULL), mIndex(0) {}
+
+const char* StringPoolRef::string8(size_t* outLen) const {
+    if (mPool != NULL) {
+        return mPool->string8At(mIndex, outLen);
     }
+    if (outLen != NULL) {
+        *outLen = 0;
+    }
+    return NULL;
+}
 
-    if ((size_t)entryIndex >= allTypes->entryCount) {
-        ALOGW("getEntry failing because entryIndex %d is beyond type entryCount %d",
-            entryIndex, (int)allTypes->entryCount);
+const char16_t* StringPoolRef::string16(size_t* outLen) const {
+    if (mPool != NULL) {
+        return mPool->stringAt(mIndex, outLen);
+    }
+    if (outLen != NULL) {
+        *outLen = 0;
+    }
+    return NULL;
+}
+
+status_t ResTable::getEntry(
+        const PackageGroup* packageGroup, int typeIndex, int entryIndex,
+        const ResTable_config* config,
+        Entry* outEntry) const
+{
+    const TypeList& typeList = packageGroup->types[typeIndex];
+    if (typeList.isEmpty()) {
+        ALOGV("Skipping entry type index 0x%02x because type is NULL!\n", typeIndex);
         return BAD_TYPE;
     }
 
-    const ResTable_type* type = NULL;
-    uint32_t offset = ResTable_type::NO_ENTRY;
+    const ResTable_type* bestType = NULL;
+    uint32_t bestOffset = ResTable_type::NO_ENTRY;
+    const Package* bestPackage = NULL;
+    uint32_t specFlags = 0;
+    uint8_t actualTypeIndex = typeIndex;
     ResTable_config bestConfig;
-    memset(&bestConfig, 0, sizeof(bestConfig)); // make the compiler shut up
+    memset(&bestConfig, 0, sizeof(bestConfig));
 
-    const size_t NT = allTypes->configs.size();
-    for (size_t i=0; i<NT; i++) {
-        const ResTable_type* const thisType = allTypes->configs[i];
-        if (thisType == NULL) continue;
+    // Iterate over the Types of each package.
+    const size_t typeCount = typeList.size();
+    for (size_t i = 0; i < typeCount; i++) {
+        const Type* const typeSpec = typeList[i];
 
-        ResTable_config thisConfig;
-        thisConfig.copyFromDtoH(thisType->config);
+        int realEntryIndex = entryIndex;
+        int realTypeIndex = typeIndex;
+        bool currentTypeIsOverlay = false;
 
-        TABLE_GETENTRY(ALOGI("Match entry 0x%x in type 0x%x (sz 0x%x): %s\n",
-                           entryIndex, typeIndex+1, dtohl(thisType->config.size),
-                           thisConfig.toString().string()));
-
-        // Check to make sure this one is valid for the current parameters.
-        if (config && !thisConfig.match(*config)) {
-            TABLE_GETENTRY(ALOGI("Does not match config!\n"));
-            continue;
-        }
-
-        // Check if there is the desired entry in this type.
-
-        const uint8_t* const end = ((const uint8_t*)thisType)
-            + dtohl(thisType->header.size);
-        const uint32_t* const eindex = (const uint32_t*)
-            (((const uint8_t*)thisType) + dtohs(thisType->header.headerSize));
-
-        uint32_t thisOffset = dtohl(eindex[entryIndex]);
-        if (thisOffset == ResTable_type::NO_ENTRY) {
-            TABLE_GETENTRY(ALOGI("Skipping because it is not defined!\n"));
-            continue;
-        }
-
-        if (type != NULL) {
-            // Check if this one is less specific than the last found.  If so,
-            // we will skip it.  We check starting with things we most care
-            // about to those we least care about.
-            if (!thisConfig.isBetterThan(bestConfig, config)) {
-                TABLE_GETENTRY(ALOGI("This config is worse than last!\n"));
+        // Runtime overlay packages provide a mapping of app resource
+        // ID to package resource ID.
+        if (typeSpec->idmapEntries.hasEntries()) {
+            uint16_t overlayEntryIndex;
+            if (typeSpec->idmapEntries.lookup(entryIndex, &overlayEntryIndex) != NO_ERROR) {
+                // No such mapping exists
                 continue;
             }
+            realEntryIndex = overlayEntryIndex;
+            realTypeIndex = typeSpec->idmapEntries.overlayTypeId() - 1;
+            currentTypeIsOverlay = true;
         }
 
-        type = thisType;
-        offset = thisOffset;
-        bestConfig = thisConfig;
-        TABLE_GETENTRY(ALOGI("Best entry so far -- using it!\n"));
-        if (!config) break;
+        if (static_cast<size_t>(realEntryIndex) >= typeSpec->entryCount) {
+            ALOGW("For resource 0x%08x, entry index(%d) is beyond type entryCount(%d)",
+                    Res_MAKEID(packageGroup->id - 1, typeIndex, entryIndex),
+                    entryIndex, static_cast<int>(typeSpec->entryCount));
+            // We should normally abort here, but some legacy apps declare
+            // resources in the 'android' package (old bug in AAPT).
+            continue;
+        }
+
+        // Aggregate all the flags for each package that defines this entry.
+        if (typeSpec->typeSpecFlags != NULL) {
+            specFlags |= dtohl(typeSpec->typeSpecFlags[realEntryIndex]);
+        } else {
+            specFlags = -1;
+        }
+
+        const size_t numConfigs = typeSpec->configs.size();
+        for (size_t c = 0; c < numConfigs; c++) {
+            const ResTable_type* const thisType = typeSpec->configs[c];
+            if (thisType == NULL) {
+                continue;
+            }
+
+            ResTable_config thisConfig;
+            thisConfig.copyFromDtoH(thisType->config);
+
+            // Check to make sure this one is valid for the current parameters.
+            if (config != NULL && !thisConfig.match(*config)) {
+                continue;
+            }
+
+            // Check if there is the desired entry in this type.
+            const uint8_t* const end = reinterpret_cast<const uint8_t*>(thisType)
+                    + dtohl(thisType->header.size);
+            const uint32_t* const eindex = reinterpret_cast<const uint32_t*>(
+                    reinterpret_cast<const uint8_t*>(thisType) + dtohs(thisType->header.headerSize));
+
+            uint32_t thisOffset = dtohl(eindex[realEntryIndex]);
+            if (thisOffset == ResTable_type::NO_ENTRY) {
+                // There is no entry for this index and configuration.
+                continue;
+            }
+
+            if (bestType != NULL) {
+                // Check if this one is less specific than the last found.  If so,
+                // we will skip it.  We check starting with things we most care
+                // about to those we least care about.
+                if (!thisConfig.isBetterThan(bestConfig, config)) {
+                    if (!currentTypeIsOverlay || thisConfig.compare(bestConfig) != 0) {
+                        continue;
+                    }
+                }
+            }
+
+            bestType = thisType;
+            bestOffset = thisOffset;
+            bestConfig = thisConfig;
+            bestPackage = typeSpec->package;
+            actualTypeIndex = realTypeIndex;
+
+            // If no config was specified, any type will do, so skip
+            if (config == NULL) {
+                break;
+            }
+        }
     }
 
-    if (type == NULL) {
-        TABLE_GETENTRY(ALOGI("No value found for requested entry!\n"));
+    if (bestType == NULL) {
         return BAD_INDEX;
     }
 
-    offset += dtohl(type->entriesStart);
-    TABLE_NOISY(ALOGD("Looking in resource table %p, typeOff=%p, offset=%p",
-            package->header->header, (void*)(((const char*)type)-((const char*)package->header->header)),
-            (void*)offset));
+    bestOffset += dtohl(bestType->entriesStart);
 
-    if (offset > (dtohl(type->header.size)-sizeof(ResTable_entry))) {
+    if (bestOffset > (dtohl(bestType->header.size)-sizeof(ResTable_entry))) {
         ALOGW("ResTable_entry at 0x%x is beyond type chunk data 0x%x",
-             offset, dtohl(type->header.size));
+                bestOffset, dtohl(bestType->header.size));
         return BAD_TYPE;
     }
-    if ((offset&0x3) != 0) {
-        ALOGW("ResTable_entry at 0x%x is not on an integer boundary",
-             offset);
+    if ((bestOffset & 0x3) != 0) {
+        ALOGW("ResTable_entry at 0x%x is not on an integer boundary", bestOffset);
         return BAD_TYPE;
     }
 
-    const ResTable_entry* const entry = (const ResTable_entry*)
-        (((const uint8_t*)type) + offset);
+    const ResTable_entry* const entry = reinterpret_cast<const ResTable_entry*>(
+            reinterpret_cast<const uint8_t*>(bestType) + bestOffset);
     if (dtohs(entry->size) < sizeof(*entry)) {
         ALOGW("ResTable_entry size 0x%x is too small", dtohs(entry->size));
         return BAD_TYPE;
     }
 
-    *outType = type;
-    *outEntry = entry;
-    if (outTypeClass != NULL) {
-        *outTypeClass = allTypes;
+    if (outEntry != NULL) {
+        outEntry->entry = entry;
+        outEntry->config = bestConfig;
+        outEntry->type = bestType;
+        outEntry->specFlags = specFlags;
+        outEntry->package = bestPackage;
+        outEntry->typeStr = StringPoolRef(&bestPackage->typeStrings, actualTypeIndex - bestPackage->typeIdOffset);
+        outEntry->keyStr = StringPoolRef(&bestPackage->keyStrings, dtohl(entry->key.index));
     }
-    return offset + dtohs(entry->size);
+    return NO_ERROR;
 }
 
 status_t ResTable::parsePackage(const ResTable_package* const pkg,
-                                const Header* const header, uint32_t idmap_id)
+                                const Header* const header)
 {
     const uint8_t* base = (const uint8_t*)pkg;
-    status_t err = validate_chunk(&pkg->header, sizeof(*pkg),
+    status_t err = validate_chunk(&pkg->header, sizeof(*pkg) - sizeof(pkg->typeIdOffset),
                                   header->dataEnd, "ResTable_package");
     if (err != NO_ERROR) {
         return (mError=err);
@@ -5494,89 +5520,88 @@
         return (mError=BAD_TYPE);
     }
 
-    Package* package = NULL;
-    PackageGroup* group = NULL;
-    uint32_t id = idmap_id != 0 ? idmap_id : dtohl(pkg->id);
-    // If at this point id == 0, pkg is an overlay package without a
-    // corresponding idmap. During regular usage, overlay packages are
-    // always loaded alongside their idmaps, but during idmap creation
-    // the package is temporarily loaded by itself.
-    if (id < 256) {
+    uint32_t id = dtohl(pkg->id);
+    KeyedVector<uint8_t, IdmapEntries> idmapEntries;
 
-        package = new Package(this, header, pkg);
-        if (package == NULL) {
+    if (header->resourceIDMap != NULL) {
+        uint8_t targetPackageId = 0;
+        status_t err = parseIdmap(header->resourceIDMap, header->resourceIDMapSize, &targetPackageId, &idmapEntries);
+        if (err != NO_ERROR) {
+            ALOGW("Overlay is broken");
+            return (mError=err);
+        }
+        id = targetPackageId;
+    }
+
+    if (id >= 256) {
+        LOG_ALWAYS_FATAL("Package id out of range");
+        return NO_ERROR;
+    } else if (id == 0) {
+        // This is a library so assign an ID
+        id = mNextPackageId++;
+    }
+
+    PackageGroup* group = NULL;
+    Package* package = new Package(this, header, pkg);
+    if (package == NULL) {
+        return (mError=NO_MEMORY);
+    }
+
+    err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
+                                   header->dataEnd-(base+dtohl(pkg->typeStrings)));
+    if (err != NO_ERROR) {
+        delete group;
+        delete package;
+        return (mError=err);
+    }
+
+    err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
+                                  header->dataEnd-(base+dtohl(pkg->keyStrings)));
+    if (err != NO_ERROR) {
+        delete group;
+        delete package;
+        return (mError=err);
+    }
+
+    size_t idx = mPackageMap[id];
+    if (idx == 0) {
+        idx = mPackageGroups.size() + 1;
+
+        char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
+        strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
+        group = new PackageGroup(this, String16(tmpName), id);
+        if (group == NULL) {
+            delete package;
             return (mError=NO_MEMORY);
         }
 
-        if (idmap_id == 0) {
-            err = package->typeStrings.setTo(base+dtohl(pkg->typeStrings),
-                                           header->dataEnd-(base+dtohl(pkg->typeStrings)));
-            if (err != NO_ERROR) {
-                delete group;
-                delete package;
-                return (mError=err);
-            }
-
-            err = package->keyStrings.setTo(base+dtohl(pkg->keyStrings),
-                                          header->dataEnd-(base+dtohl(pkg->keyStrings)));
-            if (err != NO_ERROR) {
-                delete group;
-                delete package;
-                return (mError=err);
-            }
-        }
-
-        if (id == 0) {
-            // This is a library so assign an ID
-            id = mNextPackageId++;
-        }
-
-        size_t idx = mPackageMap[id];
-        if (idx == 0) {
-            idx = mPackageGroups.size()+1;
-
-            char16_t tmpName[sizeof(pkg->name)/sizeof(char16_t)];
-            strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(char16_t));
-            group = new PackageGroup(this, String16(tmpName), id);
-            if (group == NULL) {
-                delete package;
-                return (mError=NO_MEMORY);
-            }
-
-            //printf("Adding new package id %d at index %d\n", id, idx);
-            err = mPackageGroups.add(group);
-            if (err < NO_ERROR) {
-                return (mError=err);
-            }
-            group->basePackage = package;
-
-            mPackageMap[id] = (uint8_t)idx;
-
-            // Find all packages that reference this package
-            size_t N = mPackageGroups.size();
-            for (size_t i = 0; i < N; i++) {
-                mPackageGroups[i]->dynamicRefTable.addMapping(
-                        group->name, static_cast<uint8_t>(group->id));
-            }
-        } else {
-            group = mPackageGroups.itemAt(idx-1);
-            if (group == NULL) {
-                return (mError=UNKNOWN_ERROR);
-            }
-        }
-        err = group->packages.add(package);
+        //printf("Adding new package id %d at index %d\n", id, idx);
+        err = mPackageGroups.add(group);
         if (err < NO_ERROR) {
             return (mError=err);
         }
+
+        mPackageMap[id] = static_cast<uint8_t>(idx);
+
+        // Find all packages that reference this package
+        size_t N = mPackageGroups.size();
+        for (size_t i = 0; i < N; i++) {
+            mPackageGroups[i]->dynamicRefTable.addMapping(
+                    group->name, static_cast<uint8_t>(group->id));
+        }
     } else {
-        LOG_ALWAYS_FATAL("Package id out of range");
-        return NO_ERROR;
+        group = mPackageGroups.itemAt(idx - 1);
+        if (group == NULL) {
+            return (mError=UNKNOWN_ERROR);
+        }
     }
 
+    err = group->packages.add(package);
+    if (err < NO_ERROR) {
+        return (mError=err);
+    }
 
     // Iterate through all chunks.
-    size_t curPackage = 0;
-
     const ResChunk_header* chunk =
         (const ResChunk_header*)(((const uint8_t*)pkg)
                                  + dtohs(pkg->header.headerSize));
@@ -5597,6 +5622,7 @@
             }
 
             const size_t typeSpecSize = dtohl(typeSpec->header.size);
+            const size_t newEntryCount = dtohl(typeSpec->entryCount);
 
             LOAD_TABLE_NOISY(printf("TypeSpec off %p: type=0x%x, headerSize=0x%x, size=%p\n",
                                     (void*)(base-(const uint8_t*)chunk),
@@ -5605,12 +5631,11 @@
                                     (void*)typeSpecSize));
             // look for block overrun or int overflow when multiplying by 4
             if ((dtohl(typeSpec->entryCount) > (INT32_MAX/sizeof(uint32_t))
-                    || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*dtohl(typeSpec->entryCount))
+                    || dtohs(typeSpec->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
                     > typeSpecSize)) {
                 ALOGW("ResTable_typeSpec entry index to %p extends beyond chunk end %p.",
-                     (void*)(dtohs(typeSpec->header.headerSize)
-                             +(sizeof(uint32_t)*dtohl(typeSpec->entryCount))),
-                     (void*)typeSpecSize);
+                        (void*)(dtohs(typeSpec->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
+                        (void*)typeSpecSize);
                 return (mError=BAD_TYPE);
             }
 
@@ -5619,21 +5644,36 @@
                 return (mError=BAD_TYPE);
             }
 
-            while (package->types.size() < typeSpec->id) {
-                package->types.add(NULL);
+            if (newEntryCount > 0) {
+                uint8_t typeIndex = typeSpec->id - 1;
+                ssize_t idmapIndex = idmapEntries.indexOfKey(typeSpec->id);
+                if (idmapIndex >= 0) {
+                    typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
+                }
+
+                TypeList& typeList = group->types.editItemAt(typeIndex);
+                if (!typeList.isEmpty()) {
+                    const Type* existingType = typeList[0];
+                    if (existingType->entryCount != newEntryCount && idmapIndex < 0) {
+                        ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",
+                                (int) newEntryCount, (int) existingType->entryCount);
+                        // We should normally abort here, but some legacy apps declare
+                        // resources in the 'android' package (old bug in AAPT).
+                    }
+                }
+
+                Type* t = new Type(header, package, newEntryCount);
+                t->typeSpec = typeSpec;
+                t->typeSpecFlags = (const uint32_t*)(
+                        ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
+                if (idmapIndex >= 0) {
+                    t->idmapEntries = idmapEntries[idmapIndex];
+                }
+                typeList.add(t);
+                group->largestTypeId = max(group->largestTypeId, typeSpec->id);
+            } else {
+                ALOGV("Skipping empty ResTable_typeSpec for type %d", typeSpec->id);
             }
-            Type* t = package->types[typeSpec->id-1];
-            if (t == NULL) {
-                t = new Type(header, package, dtohl(typeSpec->entryCount));
-                package->types.editItemAt(typeSpec->id-1) = t;
-            } else if (dtohl(typeSpec->entryCount) != t->entryCount) {
-                ALOGW("ResTable_typeSpec entry count inconsistent: given %d, previously %d",
-                    (int)dtohl(typeSpec->entryCount), (int)t->entryCount);
-                return (mError=BAD_TYPE);
-            }
-            t->typeSpecFlags = (const uint32_t*)(
-                    ((const uint8_t*)typeSpec) + dtohs(typeSpec->header.headerSize));
-            t->typeSpec = typeSpec;
 
         } else if (ctype == RES_TABLE_TYPE_TYPE) {
             const ResTable_type* type = (const ResTable_type*)(chunk);
@@ -5644,50 +5684,69 @@
             }
 
             const uint32_t typeSize = dtohl(type->header.size);
+            const size_t newEntryCount = dtohl(type->entryCount);
 
             LOAD_TABLE_NOISY(printf("Type off %p: type=0x%x, headerSize=0x%x, size=%p\n",
                                     (void*)(base-(const uint8_t*)chunk),
                                     dtohs(type->header.type),
                                     dtohs(type->header.headerSize),
                                     (void*)typeSize));
-            if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*dtohl(type->entryCount))
-                > typeSize) {
+            if (dtohs(type->header.headerSize)+(sizeof(uint32_t)*newEntryCount)
+                    > typeSize) {
                 ALOGW("ResTable_type entry index to %p extends beyond chunk end 0x%x.",
-                     (void*)(dtohs(type->header.headerSize)
-                             +(sizeof(uint32_t)*dtohl(type->entryCount))),
-                     typeSize);
+                        (void*)(dtohs(type->header.headerSize) + (sizeof(uint32_t)*newEntryCount)),
+                        typeSize);
                 return (mError=BAD_TYPE);
             }
-            if (dtohl(type->entryCount) != 0
+
+            if (newEntryCount != 0
                 && dtohl(type->entriesStart) > (typeSize-sizeof(ResTable_entry))) {
                 ALOGW("ResTable_type entriesStart at 0x%x extends beyond chunk end 0x%x.",
                      dtohl(type->entriesStart), typeSize);
                 return (mError=BAD_TYPE);
             }
+
             if (type->id == 0) {
                 ALOGW("ResTable_type has an id of 0.");
                 return (mError=BAD_TYPE);
             }
 
-            while (package->types.size() < type->id) {
-                package->types.add(NULL);
-            }
-            Type* t = package->types[type->id-1];
-            if (t == NULL) {
-                t = new Type(header, package, dtohl(type->entryCount));
-                package->types.editItemAt(type->id-1) = t;
-            } else if (dtohl(type->entryCount) != t->entryCount) {
-                ALOGW("ResTable_type entry count inconsistent: given %d, previously %d",
-                    (int)dtohl(type->entryCount), (int)t->entryCount);
-                return (mError=BAD_TYPE);
+            if (newEntryCount > 0) {
+                uint8_t typeIndex = type->id - 1;
+                ssize_t idmapIndex = idmapEntries.indexOfKey(type->id);
+                if (idmapIndex >= 0) {
+                    typeIndex = idmapEntries[idmapIndex].targetTypeId() - 1;
+                }
+
+                TypeList& typeList = group->types.editItemAt(typeIndex);
+                if (typeList.isEmpty()) {
+                    ALOGE("No TypeSpec for type %d", type->id);
+                    return (mError=BAD_TYPE);
+                }
+
+                Type* t = typeList.editItemAt(typeList.size() - 1);
+                if (newEntryCount != t->entryCount) {
+                    ALOGE("ResTable_type entry count inconsistent: given %d, previously %d",
+                        (int)newEntryCount, (int)t->entryCount);
+                    return (mError=BAD_TYPE);
+                }
+
+                if (t->package != package) {
+                    ALOGE("No TypeSpec for type %d", type->id);
+                    return (mError=BAD_TYPE);
+                }
+
+                t->configs.add(type);
+
+                TABLE_GETENTRY(
+                    ResTable_config thisConfig;
+                    thisConfig.copyFromDtoH(type->config);
+                    ALOGI("Adding config to type %d: %s\n",
+                          type->id, thisConfig.toString().string()));
+            } else {
+                ALOGV("Skipping empty ResTable_type for type %d", type->id);
             }
 
-            TABLE_GETENTRY(
-                ResTable_config thisConfig;
-                thisConfig.copyFromDtoH(type->config);
-                ALOGI("Adding config to type %d: %s\n",
-                      type->id, thisConfig.toString().string()));
-            t->configs.add(type);
         } else if (ctype == RES_TABLE_LIBRARY_TYPE) {
             if (group->dynamicRefTable.entries().size() == 0) {
                 status_t err = group->dynamicRefTable.load((const ResTable_lib_header*) chunk);
@@ -5714,10 +5773,6 @@
             (((const uint8_t*)chunk) + csize);
     }
 
-    if (group->typeCount == 0) {
-        group->typeCount = package->types.size();
-    }
-
     return NO_ERROR;
 }
 
@@ -5818,6 +5873,12 @@
     return NO_ERROR;
 }
 
+struct IdmapTypeMap {
+    ssize_t overlayTypeId;
+    size_t entryOffset;
+    Vector<uint32_t> entryMap;
+};
+
 status_t ResTable::createIdmap(const ResTable& overlay,
         uint32_t targetCrc, uint32_t overlayCrc,
         const char* targetPath, const char* overlayPath,
@@ -5828,41 +5889,46 @@
         ALOGW("idmap: target package has no package groups, cannot create idmap\n");
         return UNKNOWN_ERROR;
     }
+
     if (mPackageGroups[0]->packages.size() == 0) {
         ALOGW("idmap: target package has no packages in its first package group, "
                 "cannot create idmap\n");
         return UNKNOWN_ERROR;
     }
 
-    Vector<Vector<uint32_t> > map;
+    KeyedVector<uint8_t, IdmapTypeMap> map;
+
     // overlaid packages are assumed to contain only one package group
     const PackageGroup* pg = mPackageGroups[0];
-    const Package* pkg = pg->packages[0];
-    size_t typeCount = pkg->types.size();
-    // starting size is header + first item (number of types in map)
-    *outSize = (IDMAP_HEADER_SIZE + 1) * sizeof(uint32_t);
+
+    // starting size is header
+    *outSize = ResTable::IDMAP_HEADER_SIZE_BYTES;
+
+    // target package id and number of types in map
+    *outSize += 2 * sizeof(uint16_t);
+
     // overlay packages are assumed to contain only one package group
     const String16 overlayPackage(overlay.mPackageGroups[0]->packages[0]->package->name);
-    const uint32_t pkg_id = pkg->package->id << 24;
 
-    for (size_t typeIndex = 0; typeIndex < typeCount; ++typeIndex) {
-        ssize_t first = -1;
-        ssize_t last = -1;
-        const Type* typeConfigs = pkg->getType(typeIndex);
-        ssize_t mapIndex = map.add();
-        if (mapIndex < 0) {
-            return NO_MEMORY;
+    for (size_t typeIndex = 0; typeIndex < pg->types.size(); ++typeIndex) {
+        const TypeList& typeList = pg->types[typeIndex];
+        if (typeList.isEmpty()) {
+            continue;
         }
-        Vector<uint32_t>& vector = map.editItemAt(mapIndex);
+
+        const Type* typeConfigs = typeList[0];
+
+        IdmapTypeMap typeMap;
+        typeMap.overlayTypeId = -1;
+        typeMap.entryOffset = 0;
+
         for (size_t entryIndex = 0; entryIndex < typeConfigs->entryCount; ++entryIndex) {
-            uint32_t resID = pkg_id
-                | (0x00ff0000 & ((typeIndex+1)<<16))
-                | (0x0000ffff & (entryIndex));
+            uint32_t resID = Res_MAKEID(pg->id - 1, typeIndex, entryIndex);
             resource_name resName;
             if (!this->getResourceName(resID, false, &resName)) {
-                ALOGW("idmap: resource 0x%08x has spec but lacks values, skipping\n", resID);
-                // add dummy value, or trimming leading/trailing zeroes later will fail
-                vector.push(0);
+                if (typeMap.entryMap.isEmpty()) {
+                    typeMap.entryOffset++;
+                }
                 continue;
             }
 
@@ -5874,49 +5940,55 @@
                                                               overlayType.size(),
                                                               overlayPackage.string(),
                                                               overlayPackage.size());
-            if (overlayResID != 0) {
-                overlayResID = pkg_id | (0x00ffffff & overlayResID);
-                last = Res_GETENTRY(resID);
-                if (first == -1) {
-                    first = Res_GETENTRY(resID);
+            if (overlayResID == 0) {
+                if (typeMap.entryMap.isEmpty()) {
+                    typeMap.entryOffset++;
                 }
+                continue;
             }
-            vector.push(overlayResID);
-#if 0
-            if (overlayResID != 0) {
-                ALOGD("%s/%s 0x%08x -> 0x%08x\n",
-                     String8(String16(resName.type)).string(),
-                     String8(String16(resName.name)).string(),
-                     resID, overlayResID);
+
+            if (typeMap.overlayTypeId == -1) {
+                typeMap.overlayTypeId = Res_GETTYPE(overlayResID) + 1;
             }
-#endif
+
+            if (Res_GETTYPE(overlayResID) + 1 != static_cast<size_t>(typeMap.overlayTypeId)) {
+                ALOGE("idmap: can't mix type ids in entry map. Resource 0x%08x maps to 0x%08x"
+                        " but entries should map to resources of type %02x",
+                        resID, overlayResID, typeMap.overlayTypeId);
+                return BAD_TYPE;
+            }
+
+            if (typeMap.entryOffset + typeMap.entryMap.size() < entryIndex) {
+                // Resize to accomodate this entry and the 0's in between.
+                if (typeMap.entryMap.resize((entryIndex - typeMap.entryOffset) + 1) < 0) {
+                    return NO_MEMORY;
+                }
+                typeMap.entryMap.editTop() = Res_GETENTRY(overlayResID);
+            } else {
+                typeMap.entryMap.add(Res_GETENTRY(overlayResID));
+            }
         }
 
-        if (first != -1) {
-            // shave off trailing entries which lack overlay values
-            const size_t last_past_one = last + 1;
-            if (last_past_one < vector.size()) {
-                vector.removeItemsAt(last_past_one, vector.size() - last_past_one);
+        if (!typeMap.entryMap.isEmpty()) {
+            if (map.add(static_cast<uint8_t>(typeIndex), typeMap) < 0) {
+                return NO_MEMORY;
             }
-            // shave off leading entries which lack overlay values
-            vector.removeItemsAt(0, first);
-            // store offset to first overlaid resource ID of this type
-            vector.insertAt((uint32_t)first, 0, 1);
-            // reserve space for number and offset of entries, and the actual entries
-            *outSize += (2 + vector.size()) * sizeof(uint32_t);
-        } else {
-            // no entries of current type defined in overlay package
-            vector.clear();
-            // reserve space for type offset
-            *outSize += 1 * sizeof(uint32_t);
+            *outSize += (4 * sizeof(uint16_t)) + (typeMap.entryMap.size() * sizeof(uint32_t));
         }
     }
 
+    if (map.isEmpty()) {
+        ALOGW("idmap: no resources in overlay package present in base package");
+        return UNKNOWN_ERROR;
+    }
+
     if ((*outData = malloc(*outSize)) == NULL) {
         return NO_MEMORY;
     }
+
     uint32_t* data = (uint32_t*)*outData;
     *data++ = htodl(IDMAP_MAGIC);
+    *data++ = htodl(IDMAP_CURRENT_VERSION);
     *data++ = htodl(targetCrc);
     *data++ = htodl(overlayCrc);
     const char* paths[] = { targetPath, overlayPath };
@@ -5934,44 +6006,30 @@
         data += 256 / sizeof(uint32_t);
     }
     const size_t mapSize = map.size();
-    *data++ = htodl(mapSize);
-    size_t offset = mapSize;
+    uint16_t* typeData = reinterpret_cast<uint16_t*>(data);
+    *typeData++ = htods(pg->id);
+    *typeData++ = htods(mapSize);
     for (size_t i = 0; i < mapSize; ++i) {
-        const Vector<uint32_t>& vector = map.itemAt(i);
-        const size_t N = vector.size();
-        if (N == 0) {
-            *data++ = htodl(0);
-        } else {
-            offset++;
-            *data++ = htodl(offset);
-            offset += N;
+        uint8_t targetTypeId = map.keyAt(i);
+        const IdmapTypeMap& typeMap = map[i];
+        *typeData++ = htods(targetTypeId + 1);
+        *typeData++ = htods(typeMap.overlayTypeId);
+        *typeData++ = htods(typeMap.entryMap.size());
+        *typeData++ = htods(typeMap.entryOffset);
+
+        const size_t entryCount = typeMap.entryMap.size();
+        uint32_t* entries = reinterpret_cast<uint32_t*>(typeData);
+        for (size_t j = 0; j < entryCount; j++) {
+            entries[j] = htodl(typeMap.entryMap[j]);
         }
-    }
-    if (offset == mapSize) {
-        ALOGW("idmap: no resources in overlay package present in base package\n");
-        return UNKNOWN_ERROR;
-    }
-    for (size_t i = 0; i < mapSize; ++i) {
-        const Vector<uint32_t>& vector = map.itemAt(i);
-        const size_t N = vector.size();
-        if (N == 0) {
-            continue;
-        }
-        if (N == 1) { // vector expected to hold (offset) + (N > 0 entries)
-            ALOGW("idmap: type %u supposedly has entries, but no entries found\n", (uint32_t)i);
-            return UNKNOWN_ERROR;
-        }
-        *data++ = htodl(N - 1); // do not count the offset (which is vector's first element)
-        for (size_t j = 0; j < N; ++j) {
-            const uint32_t& overlayResID = vector.itemAt(j);
-            *data++ = htodl(overlayResID);
-        }
+        typeData += entryCount * 2;
     }
 
     return NO_ERROR;
 }
 
 bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes,
+                            uint32_t* pVersion,
                             uint32_t* pTargetCrc, uint32_t* pOverlayCrc,
                             String8* pTargetPath, String8* pOverlayPath)
 {
@@ -5979,17 +6037,20 @@
     if (!assertIdmapHeader(map, sizeBytes)) {
         return false;
     }
+    if (pVersion) {
+        *pVersion = dtohl(map[1]);
+    }
     if (pTargetCrc) {
-        *pTargetCrc = map[1];
+        *pTargetCrc = dtohl(map[2]);
     }
     if (pOverlayCrc) {
-        *pOverlayCrc = map[2];
+        *pOverlayCrc = dtohl(map[3]);
     }
     if (pTargetPath) {
-        pTargetPath->setTo(reinterpret_cast<const char*>(map + 3));
+        pTargetPath->setTo(reinterpret_cast<const char*>(map + 4));
     }
     if (pOverlayPath) {
-        pOverlayPath->setTo(reinterpret_cast<const char*>(map + 3 + 256 / sizeof(uint32_t)));
+        pOverlayPath->setTo(reinterpret_cast<const char*>(map + 4 + 256 / sizeof(uint32_t)));
     }
     return true;
 }
@@ -6138,184 +6199,184 @@
         size_t pkgCount = pg->packages.size();
         for (size_t pkgIndex=0; pkgIndex<pkgCount; pkgIndex++) {
             const Package* pkg = pg->packages[pkgIndex];
-            size_t typeCount = pkg->types.size();
-            printf("  Package %d id=%d name=%s typeCount=%d\n", (int)pkgIndex,
-                    pkg->package->id, String8(String16(pkg->package->name)).string(),
-                    (int)typeCount);
-            for (size_t typeIndex=0; typeIndex<typeCount; typeIndex++) {
-                const Type* typeConfigs = pkg->getType(typeIndex);
-                if (typeConfigs == NULL) {
-                    printf("    type %d NULL\n", (int)typeIndex);
+            printf("  Package %d id=%d name=%s\n", (int)pkgIndex,
+                    pkg->package->id, String8(String16(pkg->package->name)).string());
+        }
+
+        for (size_t typeIndex=0; typeIndex < pg->types.size(); typeIndex++) {
+            const TypeList& typeList = pg->types[typeIndex];
+            if (typeList.isEmpty()) {
+                //printf("    type %d NULL\n", (int)typeIndex);
+                continue;
+            }
+            const Type* typeConfigs = typeList[0];
+            const size_t NTC = typeConfigs->configs.size();
+            printf("    type %d configCount=%d entryCount=%d\n",
+                   (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
+            if (typeConfigs->typeSpecFlags != NULL) {
+                for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
+                    uint32_t resID = (0xff000000 & ((pg->id)<<24))
+                                | (0x00ff0000 & ((typeIndex+1)<<16))
+                                | (0x0000ffff & (entryIndex));
+                    // Since we are creating resID without actually
+                    // iterating over them, we have no idea which is a
+                    // dynamic reference. We must check.
+                    pg->dynamicRefTable.lookupResourceId(&resID);
+
+                    resource_name resName;
+                    if (this->getResourceName(resID, true, &resName)) {
+                        String8 type8;
+                        String8 name8;
+                        if (resName.type8 != NULL) {
+                            type8 = String8(resName.type8, resName.typeLen);
+                        } else {
+                            type8 = String8(resName.type, resName.typeLen);
+                        }
+                        if (resName.name8 != NULL) {
+                            name8 = String8(resName.name8, resName.nameLen);
+                        } else {
+                            name8 = String8(resName.name, resName.nameLen);
+                        }
+                        printf("      spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
+                            resID,
+                            CHAR16_TO_CSTR(resName.package, resName.packageLen),
+                            type8.string(), name8.string(),
+                            dtohl(typeConfigs->typeSpecFlags[entryIndex]));
+                    } else {
+                        printf("      INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
+                    }
+                }
+            }
+            for (size_t configIndex=0; configIndex<NTC; configIndex++) {
+                const ResTable_type* type = typeConfigs->configs[configIndex];
+                if ((((uint64_t)type)&0x3) != 0) {
+                    printf("      NON-INTEGER ResTable_type ADDRESS: %p\n", type);
                     continue;
                 }
-                const size_t NTC = typeConfigs->configs.size();
-                printf("    type %d configCount=%d entryCount=%d\n",
-                       (int)typeIndex, (int)NTC, (int)typeConfigs->entryCount);
-                if (typeConfigs->typeSpecFlags != NULL) {
-                    for (size_t entryIndex=0; entryIndex<typeConfigs->entryCount; entryIndex++) {
-                        uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
-                                    | (0x00ff0000 & ((typeIndex+1)<<16))
-                                    | (0x0000ffff & (entryIndex));
-                        // Since we are creating resID without actually
-                        // iterating over them, we have no idea which is a
-                        // dynamic reference. We must check.
-                        pg->dynamicRefTable.lookupResourceId(&resID);
-
-                        resource_name resName;
-                        if (this->getResourceName(resID, true, &resName)) {
-                            String8 type8;
-                            String8 name8;
-                            if (resName.type8 != NULL) {
-                                type8 = String8(resName.type8, resName.typeLen);
-                            } else {
-                                type8 = String8(resName.type, resName.typeLen);
-                            }
-                            if (resName.name8 != NULL) {
-                                name8 = String8(resName.name8, resName.nameLen);
-                            } else {
-                                name8 = String8(resName.name, resName.nameLen);
-                            }
-                            printf("      spec resource 0x%08x %s:%s/%s: flags=0x%08x\n",
-                                resID,
-                                CHAR16_TO_CSTR(resName.package, resName.packageLen),
-                                type8.string(), name8.string(),
-                                dtohl(typeConfigs->typeSpecFlags[entryIndex]));
-                        } else {
-                            printf("      INVALID TYPE CONFIG FOR RESOURCE 0x%08x\n", resID);
-                        }
-                    }
+                String8 configStr = type->config.toString();
+                printf("      config %s:\n", configStr.size() > 0
+                        ? configStr.string() : "(default)");
+                size_t entryCount = dtohl(type->entryCount);
+                uint32_t entriesStart = dtohl(type->entriesStart);
+                if ((entriesStart&0x3) != 0) {
+                    printf("      NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart);
+                    continue;
                 }
-                for (size_t configIndex=0; configIndex<NTC; configIndex++) {
-                    const ResTable_type* type = typeConfigs->configs[configIndex];
-                    if ((((uint64_t)type)&0x3) != 0) {
-                        printf("      NON-INTEGER ResTable_type ADDRESS: %p\n", type);
+                uint32_t typeSize = dtohl(type->header.size);
+                if ((typeSize&0x3) != 0) {
+                    printf("      NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
+                    continue;
+                }
+                for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
+
+                    const uint8_t* const end = ((const uint8_t*)type)
+                        + dtohl(type->header.size);
+                    const uint32_t* const eindex = (const uint32_t*)
+                        (((const uint8_t*)type) + dtohs(type->header.headerSize));
+
+                    uint32_t thisOffset = dtohl(eindex[entryIndex]);
+                    if (thisOffset == ResTable_type::NO_ENTRY) {
                         continue;
                     }
-                    String8 configStr = type->config.toString();
-                    printf("      config %s:\n", configStr.size() > 0
-                            ? configStr.string() : "(default)");
-                    size_t entryCount = dtohl(type->entryCount);
-                    uint32_t entriesStart = dtohl(type->entriesStart);
-                    if ((entriesStart&0x3) != 0) {
-                        printf("      NON-INTEGER ResTable_type entriesStart OFFSET: 0x%x\n", entriesStart);
-                        continue;
-                    }
-                    uint32_t typeSize = dtohl(type->header.size);
-                    if ((typeSize&0x3) != 0) {
-                        printf("      NON-INTEGER ResTable_type header.size: 0x%x\n", typeSize);
-                        continue;
-                    }
-                    for (size_t entryIndex=0; entryIndex<entryCount; entryIndex++) {
 
-                        const uint8_t* const end = ((const uint8_t*)type)
-                            + dtohl(type->header.size);
-                        const uint32_t* const eindex = (const uint32_t*)
-                            (((const uint8_t*)type) + dtohs(type->header.headerSize));
-
-                        uint32_t thisOffset = dtohl(eindex[entryIndex]);
-                        if (thisOffset == ResTable_type::NO_ENTRY) {
-                            continue;
-                        }
-
-                        uint32_t resID = (0xff000000 & ((pkg->package->id)<<24))
-                                    | (0x00ff0000 & ((typeIndex+1)<<16))
-                                    | (0x0000ffff & (entryIndex));
-                        pg->dynamicRefTable.lookupResourceId(&resID);
-                        resource_name resName;
-                        if (this->getResourceName(resID, true, &resName)) {
-                            String8 type8;
-                            String8 name8;
-                            if (resName.type8 != NULL) {
-                                type8 = String8(resName.type8, resName.typeLen);
-                            } else {
-                                type8 = String8(resName.type, resName.typeLen);
-                            }
-                            if (resName.name8 != NULL) {
-                                name8 = String8(resName.name8, resName.nameLen);
-                            } else {
-                                name8 = String8(resName.name, resName.nameLen);
-                            }
-                            printf("        resource 0x%08x %s:%s/%s: ", resID,
-                                    CHAR16_TO_CSTR(resName.package, resName.packageLen),
-                                    type8.string(), name8.string());
+                    uint32_t resID = (0xff000000 & ((pg->id)<<24))
+                                | (0x00ff0000 & ((typeIndex+1)<<16))
+                                | (0x0000ffff & (entryIndex));
+                    pg->dynamicRefTable.lookupResourceId(&resID);
+                    resource_name resName;
+                    if (this->getResourceName(resID, true, &resName)) {
+                        String8 type8;
+                        String8 name8;
+                        if (resName.type8 != NULL) {
+                            type8 = String8(resName.type8, resName.typeLen);
                         } else {
-                            printf("        INVALID RESOURCE 0x%08x: ", resID);
+                            type8 = String8(resName.type, resName.typeLen);
                         }
-                        if ((thisOffset&0x3) != 0) {
-                            printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
-                            continue;
-                        }
-                        if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
-                            printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
-                                   entriesStart, thisOffset, typeSize);
-                            continue;
-                        }
-
-                        const ResTable_entry* ent = (const ResTable_entry*)
-                            (((const uint8_t*)type) + entriesStart + thisOffset);
-                        if (((entriesStart + thisOffset)&0x3) != 0) {
-                            printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
-                                 (entriesStart + thisOffset));
-                            continue;
-                        }
-
-                        uintptr_t esize = dtohs(ent->size);
-                        if ((esize&0x3) != 0) {
-                            printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
-                            continue;
-                        }
-                        if ((thisOffset+esize) > typeSize) {
-                            printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
-                                   entriesStart, thisOffset, (void *)esize, typeSize);
-                            continue;
-                        }
-
-                        const Res_value* valuePtr = NULL;
-                        const ResTable_map_entry* bagPtr = NULL;
-                        Res_value value;
-                        if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
-                            printf("<bag>");
-                            bagPtr = (const ResTable_map_entry*)ent;
+                        if (resName.name8 != NULL) {
+                            name8 = String8(resName.name8, resName.nameLen);
                         } else {
-                            valuePtr = (const Res_value*)
-                                (((const uint8_t*)ent) + esize);
-                            value.copyFrom_dtoh(*valuePtr);
-                            printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
-                                   (int)value.dataType, (int)value.data,
-                                   (int)value.size, (int)value.res0);
+                            name8 = String8(resName.name, resName.nameLen);
                         }
+                        printf("        resource 0x%08x %s:%s/%s: ", resID,
+                                CHAR16_TO_CSTR(resName.package, resName.packageLen),
+                                type8.string(), name8.string());
+                    } else {
+                        printf("        INVALID RESOURCE 0x%08x: ", resID);
+                    }
+                    if ((thisOffset&0x3) != 0) {
+                        printf("NON-INTEGER OFFSET: 0x%x\n", thisOffset);
+                        continue;
+                    }
+                    if ((thisOffset+sizeof(ResTable_entry)) > typeSize) {
+                        printf("OFFSET OUT OF BOUNDS: 0x%x+0x%x (size is 0x%x)\n",
+                               entriesStart, thisOffset, typeSize);
+                        continue;
+                    }
 
-                        if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
-                            printf(" (PUBLIC)");
-                        }
-                        printf("\n");
+                    const ResTable_entry* ent = (const ResTable_entry*)
+                        (((const uint8_t*)type) + entriesStart + thisOffset);
+                    if (((entriesStart + thisOffset)&0x3) != 0) {
+                        printf("NON-INTEGER ResTable_entry OFFSET: 0x%x\n",
+                             (entriesStart + thisOffset));
+                        continue;
+                    }
 
-                        if (inclValues) {
-                            if (valuePtr != NULL) {
-                                printf("          ");
-                                print_value(pkg, value);
-                            } else if (bagPtr != NULL) {
-                                const int N = dtohl(bagPtr->count);
-                                const uint8_t* baseMapPtr = (const uint8_t*)ent;
-                                size_t mapOffset = esize;
-                                const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
-                                const uint32_t parent = dtohl(bagPtr->parent.ident);
-                                uint32_t resolvedParent = parent;
-                                status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent);
-                                if (err != NO_ERROR) {
-                                    resolvedParent = 0;
-                                }
-                                printf("          Parent=0x%08x(Resolved=0x%08x), Count=%d\n",
-                                        parent, resolvedParent, N);
-                                for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
-                                    printf("          #%i (Key=0x%08x): ",
-                                        i, dtohl(mapPtr->name.ident));
-                                    value.copyFrom_dtoh(mapPtr->value);
-                                    print_value(pkg, value);
-                                    const size_t size = dtohs(mapPtr->value.size);
-                                    mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
-                                    mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
-                                }
+                    uintptr_t esize = dtohs(ent->size);
+                    if ((esize&0x3) != 0) {
+                        printf("NON-INTEGER ResTable_entry SIZE: %p\n", (void *)esize);
+                        continue;
+                    }
+                    if ((thisOffset+esize) > typeSize) {
+                        printf("ResTable_entry OUT OF BOUNDS: 0x%x+0x%x+%p (size is 0x%x)\n",
+                               entriesStart, thisOffset, (void *)esize, typeSize);
+                        continue;
+                    }
+
+                    const Res_value* valuePtr = NULL;
+                    const ResTable_map_entry* bagPtr = NULL;
+                    Res_value value;
+                    if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) {
+                        printf("<bag>");
+                        bagPtr = (const ResTable_map_entry*)ent;
+                    } else {
+                        valuePtr = (const Res_value*)
+                            (((const uint8_t*)ent) + esize);
+                        value.copyFrom_dtoh(*valuePtr);
+                        printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)",
+                               (int)value.dataType, (int)value.data,
+                               (int)value.size, (int)value.res0);
+                    }
+
+                    if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) {
+                        printf(" (PUBLIC)");
+                    }
+                    printf("\n");
+
+                    if (inclValues) {
+                        if (valuePtr != NULL) {
+                            printf("          ");
+                            print_value(typeConfigs->package, value);
+                        } else if (bagPtr != NULL) {
+                            const int N = dtohl(bagPtr->count);
+                            const uint8_t* baseMapPtr = (const uint8_t*)ent;
+                            size_t mapOffset = esize;
+                            const ResTable_map* mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
+                            const uint32_t parent = dtohl(bagPtr->parent.ident);
+                            uint32_t resolvedParent = parent;
+                            status_t err = pg->dynamicRefTable.lookupResourceId(&resolvedParent);
+                            if (err != NO_ERROR) {
+                                resolvedParent = 0;
+                            }
+                            printf("          Parent=0x%08x(Resolved=0x%08x), Count=%d\n",
+                                    parent, resolvedParent, N);
+                            for (int i=0; i<N && mapOffset < (typeSize-sizeof(ResTable_map)); i++) {
+                                printf("          #%i (Key=0x%08x): ",
+                                    i, dtohl(mapPtr->name.ident));
+                                value.copyFrom_dtoh(mapPtr->value);
+                                print_value(typeConfigs->package, value);
+                                const size_t size = dtohs(mapPtr->value.size);
+                                mapOffset += size + sizeof(*mapPtr)-sizeof(mapPtr->value);
+                                mapPtr = (ResTable_map*)(baseMapPtr+mapOffset);
                             }
                         }
                     }
diff --git a/libs/androidfw/TypeWrappers.cpp b/libs/androidfw/TypeWrappers.cpp
new file mode 100644
index 0000000..06b4040
--- /dev/null
+++ b/libs/androidfw/TypeWrappers.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/TypeWrappers.h>
+
+namespace android {
+
+TypeVariant::iterator& TypeVariant::iterator::operator++() {
+    mIndex++;
+    if (mIndex > dtohl(mTypeVariant->data->entryCount)) {
+        mIndex = dtohl(mTypeVariant->data->entryCount);
+    }
+    return *this;
+}
+
+const ResTable_entry* TypeVariant::iterator::operator*() const {
+    const ResTable_type* type = mTypeVariant->data;
+    const uint32_t entryCount = dtohl(type->entryCount);
+    if (mIndex >= entryCount) {
+        return NULL;
+    }
+
+    const uintptr_t containerEnd = reinterpret_cast<uintptr_t>(type)
+            + dtohl(type->header.size);
+    const uint32_t* const entryIndices = reinterpret_cast<const uint32_t*>(
+            reinterpret_cast<uintptr_t>(type) + dtohs(type->header.headerSize));
+    if (reinterpret_cast<uintptr_t>(entryIndices) + (sizeof(uint32_t) * entryCount) > containerEnd) {
+        ALOGE("Type's entry indices extend beyond its boundaries");
+        return NULL;
+    }
+
+    const uint32_t entryOffset = dtohl(entryIndices[mIndex]);
+    if (entryOffset == ResTable_type::NO_ENTRY) {
+        return NULL;
+    }
+
+    if ((entryOffset & 0x3) != 0) {
+        ALOGE("Index %u points to entry with unaligned offset 0x%08x", mIndex, entryOffset);
+        return NULL;
+    }
+
+    const ResTable_entry* entry = reinterpret_cast<const ResTable_entry*>(
+            reinterpret_cast<uintptr_t>(type) + dtohl(type->entriesStart) + entryOffset);
+    if (reinterpret_cast<uintptr_t>(entry) > containerEnd - sizeof(*entry)) {
+        ALOGE("Entry offset at index %u points outside the Type's boundaries", mIndex);
+        return NULL;
+    } else if (reinterpret_cast<uintptr_t>(entry) + dtohs(entry->size) > containerEnd) {
+        ALOGE("Entry at index %u extends beyond Type's boundaries", mIndex);
+        return NULL;
+    } else if (dtohs(entry->size) < sizeof(*entry)) {
+        ALOGE("Entry at index %u is too small (%u)", mIndex, dtohs(entry->size));
+        return NULL;
+    }
+    return entry;
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk
index 9e9649c..4ff6eec 100644
--- a/libs/androidfw/tests/Android.mk
+++ b/libs/androidfw/tests/Android.mk
@@ -1,33 +1,66 @@
-# Build the unit tests.
+#
+# Copyright (C) 2014 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.
+#
+
+# ==========================================================
+# Setup some common variables for the different build
+# targets here.
+# ==========================================================
 LOCAL_PATH:= $(call my-dir)
+testFiles := \
+    ByteBucketArray_test.cpp \
+    Idmap_test.cpp \
+    ResourceTypes_test.cpp \
+    ResTable_test.cpp \
+    Split_test.cpp \
+    TypeWrappers_test.cpp \
+    ZipUtils_test.cpp
+
+# ==========================================================
+# Build the host tests: libandroidfw_tests
+# ==========================================================
 include $(CLEAR_VARS)
 
-# Build the unit tests.
-test_src_files := \
-    BackupData_test.cpp \
-    ObbFile_test.cpp \
-    ZipUtils_test.cpp \
-    ResourceTypes_test.cpp
+LOCAL_MODULE := libandroidfw_tests
 
-shared_libraries := \
+LOCAL_SRC_FILES := $(testFiles)
+LOCAL_STATIC_LIBRARIES := \
+    libandroidfw \
+    libutils \
+    libcutils \
+	liblog
+
+include $(BUILD_HOST_NATIVE_TEST)
+
+
+# ==========================================================
+# Build the device tests: libandroidfw_tests
+# ==========================================================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libandroidfw_tests
+
+LOCAL_SRC_FILES := $(testFiles) \
+    BackupData_test.cpp \
+    ObbFile_test.cpp
+
+LOCAL_SHARED_LIBRARIES := \
     libandroidfw \
     libcutils \
     libutils \
     libui \
     libstlport
 
-static_libraries := \
-    libgtest \
-    libgtest_main
-
-$(foreach file,$(test_src_files), \
-    $(eval include $(CLEAR_VARS)) \
-    $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
-    $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
-    $(eval LOCAL_SRC_FILES := $(file)) \
-    $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
-    $(eval include $(BUILD_NATIVE_TEST)) \
-)
-
-# Build the manual test programs.
-include $(call all-makefiles-under, $(LOCAL_PATH))
+include $(BUILD_NATIVE_TEST)
diff --git a/libs/androidfw/tests/ByteBucketArray_test.cpp b/libs/androidfw/tests/ByteBucketArray_test.cpp
new file mode 100644
index 0000000..376e79c
--- /dev/null
+++ b/libs/androidfw/tests/ByteBucketArray_test.cpp
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/ByteBucketArray.h>
+
+#include <gtest/gtest.h>
+
+using android::ByteBucketArray;
+
+TEST(ByteBucketArrayTest, TestSparseInsertion) {
+    ByteBucketArray<int> bba;
+    ASSERT_TRUE(bba.set(0, 1));
+    ASSERT_TRUE(bba.set(10, 2));
+    ASSERT_TRUE(bba.set(26, 3));
+    ASSERT_TRUE(bba.set(129, 4));
+    ASSERT_TRUE(bba.set(234, 5));
+
+    for (size_t i = 0; i < bba.size(); i++) {
+        switch (i) {
+            case 0: EXPECT_EQ(1, bba[i]); break;
+            case 10: EXPECT_EQ(2, bba[i]); break;
+            case 26: EXPECT_EQ(3, bba[i]); break;
+            case 129: EXPECT_EQ(4, bba[i]); break;
+            case 234: EXPECT_EQ(5, bba[i]); break;
+            default: EXPECT_EQ(0, bba[i]); break;
+        }
+    }
+}
diff --git a/libs/androidfw/tests/Idmap_test.cpp b/libs/androidfw/tests/Idmap_test.cpp
new file mode 100644
index 0000000..d829b76
--- /dev/null
+++ b/libs/androidfw/tests/Idmap_test.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+/**
+ * Include a binary resource table.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/basic/basic_arsc.h"
+
+/**
+ * Include a binary resource table.
+ * This table is an overlay.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/overlay/overlay_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+static const uint32_t attr_attr1            = 0x7f010000;
+static const uint32_t attr_attr2            = 0x7f010001;
+static const uint32_t string_test1          = 0x7f020000;
+static const uint32_t string_test2          = 0x7f020001;
+static const uint32_t integer_number1       = 0x7f030000;
+static const uint32_t integer_number2       = 0x7f030001;
+static const uint32_t style_Theme1          = 0x7f040000;
+static const uint32_t style_Theme2          = 0x7f040001;
+static const uint32_t array_integerArray1   = 0x7f050000;
+
+class IdmapTest : public ::testing::Test {
+protected:
+    virtual void SetUp() {
+        ASSERT_EQ(NO_ERROR, mTargetTable.add(basic_arsc, basic_arsc_len));
+        ASSERT_EQ(NO_ERROR, mOverlayTable.add(overlay_arsc, overlay_arsc_len));
+        char targetName[256] = "com.android.test.basic";
+        ASSERT_EQ(NO_ERROR, mTargetTable.createIdmap(mOverlayTable, 0, 0,
+                    targetName, targetName, &mData, &mDataSize));
+    }
+
+    virtual void TearDown() {
+        free(mData);
+    }
+
+    ResTable mTargetTable;
+    ResTable mOverlayTable;
+    void* mData;
+    size_t mDataSize;
+};
+
+TEST_F(IdmapTest, canLoadIdmap) {
+    ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+}
+
+TEST_F(IdmapTest, overlayOverridesResourceValue) {
+    Res_value val;
+    ssize_t block = mTargetTable.getResource(string_test2, &val, false);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+    const ResStringPool* pool = mTargetTable.getTableStringBlock(block);
+    ASSERT_TRUE(pool != NULL);
+    ASSERT_LT(val.data, pool->size());
+
+    size_t strLen;
+    const char16_t* targetStr16 = pool->stringAt(val.data, &strLen);
+    ASSERT_TRUE(targetStr16 != NULL);
+    ASSERT_EQ(String16("test2"), String16(targetStr16, strLen));
+
+    ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+
+    ssize_t newBlock = mTargetTable.getResource(string_test2, &val, false);
+    ASSERT_GE(newBlock, 0);
+    ASSERT_NE(block, newBlock);
+    ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+    pool = mTargetTable.getTableStringBlock(newBlock);
+    ASSERT_TRUE(pool != NULL);
+    ASSERT_LT(val.data, pool->size());
+
+    targetStr16 = pool->stringAt(val.data, &strLen);
+    ASSERT_TRUE(targetStr16 != NULL);
+    ASSERT_EQ(String16("test2-overlay"), String16(targetStr16, strLen));
+}
+
+TEST_F(IdmapTest, overlaidResourceHasSameName) {
+    ASSERT_EQ(NO_ERROR, mTargetTable.add(overlay_arsc, overlay_arsc_len, mData, mDataSize));
+
+    ResTable::resource_name resName;
+    ASSERT_TRUE(mTargetTable.getResourceName(array_integerArray1, false, &resName));
+
+    ASSERT_TRUE(resName.package != NULL);
+    ASSERT_TRUE(resName.type != NULL);
+    ASSERT_TRUE(resName.name != NULL);
+
+    EXPECT_EQ(String16("com.android.test.basic"), String16(resName.package, resName.packageLen));
+    EXPECT_EQ(String16("array"), String16(resName.type, resName.typeLen));
+    EXPECT_EQ(String16("integerArray1"), String16(resName.name, resName.nameLen));
+}
+
+} // namespace
diff --git a/libs/androidfw/tests/ResTable_test.cpp b/libs/androidfw/tests/ResTable_test.cpp
new file mode 100644
index 0000000..54d42c3
--- /dev/null
+++ b/libs/androidfw/tests/ResTable_test.cpp
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+namespace {
+
+/**
+ * Include a binary resource table.
+ *
+ * Package: com.android.test.basic
+ */
+#include "data/basic/basic_arsc.h"
+
+enum { MAY_NOT_BE_BAG = false };
+
+static const uint32_t attr_attr1            = 0x7f010000;
+static const uint32_t attr_attr2            = 0x7f010001;
+static const uint32_t string_test1          = 0x7f020000;
+static const uint32_t string_test2          = 0x7f020001;
+static const uint32_t integer_number1       = 0x7f030000;
+static const uint32_t integer_number2       = 0x7f030001;
+static const uint32_t style_Theme1          = 0x7f040000;
+static const uint32_t style_Theme2          = 0x7f040001;
+static const uint32_t array_integerArray1   = 0x7f050000;
+
+TEST(ResTableTest, shouldLoadSuccessfully) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+}
+
+TEST(ResTableTest, simpleTypeIsRetrievedCorrectly) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    Res_value val;
+    ssize_t block = table.getResource(string_test1, &val, MAY_NOT_BE_BAG);
+
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_STRING, val.dataType);
+
+    const ResStringPool* pool = table.getTableStringBlock(block);
+    ASSERT_TRUE(NULL != pool);
+    ASSERT_EQ(String8("test1"), pool->string8ObjectAt(val.data));
+}
+
+TEST(ResTableTest, resourceNameIsResolved) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    String16 defPackage("com.android.test.basic");
+    String16 testName("@string/test1");
+    uint32_t resID = table.identifierForName(testName.string(), testName.size(),
+                                             0, 0,
+                                             defPackage.string(), defPackage.size());
+    ASSERT_NE(uint32_t(0x00000000), resID);
+    ASSERT_EQ(string_test1, resID);
+}
+
+TEST(ResTableTest, noParentThemeIsAppliedCorrectly) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    ResTable::Theme theme(table);
+    ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme1));
+
+    Res_value val;
+    uint32_t specFlags = 0;
+    ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags);
+    ASSERT_GE(index, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+    ASSERT_EQ(uint32_t(100), val.data);
+
+    index = theme.getAttribute(attr_attr2, &val, &specFlags);
+    ASSERT_GE(index, 0);
+    ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+    ASSERT_EQ(integer_number1, val.data);
+}
+
+TEST(ResTableTest, parentThemeIsAppliedCorrectly) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    ResTable::Theme theme(table);
+    ASSERT_EQ(NO_ERROR, theme.applyStyle(style_Theme2));
+
+    Res_value val;
+    uint32_t specFlags = 0;
+    ssize_t index = theme.getAttribute(attr_attr1, &val, &specFlags);
+    ASSERT_GE(index, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+    ASSERT_EQ(uint32_t(300), val.data);
+
+    index = theme.getAttribute(attr_attr2, &val, &specFlags);
+    ASSERT_GE(index, 0);
+    ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+    ASSERT_EQ(integer_number1, val.data);
+}
+
+TEST(ResTableTest, referenceToBagIsNotResolved) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    Res_value val;
+    ssize_t block = table.getResource(integer_number2, &val, MAY_NOT_BE_BAG);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+    ASSERT_EQ(array_integerArray1, val.data);
+
+    ssize_t newBlock = table.resolveReference(&val, block);
+    EXPECT_EQ(block, newBlock);
+    EXPECT_EQ(Res_value::TYPE_REFERENCE, val.dataType);
+    EXPECT_EQ(array_integerArray1, val.data);
+}
+
+TEST(ResTableTest, resourcesStillAccessibleAfterParameterChange) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    Res_value val;
+    ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+
+    const ResTable::bag_entry* entry;
+    ssize_t count = table.lockBag(array_integerArray1, &entry);
+    ASSERT_GE(count, 0);
+    table.unlockBag(entry);
+
+    ResTable_config param;
+    memset(&param, 0, sizeof(param));
+    param.density = 320;
+    table.setParameters(&param);
+
+    block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+
+    count = table.lockBag(array_integerArray1, &entry);
+    ASSERT_GE(count, 0);
+    table.unlockBag(entry);
+}
+
+TEST(ResTableTest, resourceIsOverridenWithBetterConfig) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(basic_arsc, basic_arsc_len));
+
+    Res_value val;
+    ssize_t block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+    ASSERT_EQ(uint32_t(200), val.data);
+
+    ResTable_config param;
+    memset(&param, 0, sizeof(param));
+    param.language[0] = 's';
+    param.language[1] = 'v';
+    param.country[0] = 'S';
+    param.country[1] = 'E';
+    table.setParameters(&param);
+
+    block = table.getResource(integer_number1, &val, MAY_NOT_BE_BAG);
+    ASSERT_GE(block, 0);
+    ASSERT_EQ(Res_value::TYPE_INT_DEC, val.dataType);
+    ASSERT_EQ(uint32_t(400), val.data);
+}
+
+}
diff --git a/libs/androidfw/tests/ResourceTypes_test.cpp b/libs/androidfw/tests/ResourceTypes_test.cpp
index 4888b4a..6041e08 100644
--- a/libs/androidfw/tests/ResourceTypes_test.cpp
+++ b/libs/androidfw/tests/ResourceTypes_test.cpp
@@ -64,8 +64,8 @@
      config.packLanguage("eng");
 
      // 1-00110-01 101-00100
-     EXPECT_EQ(0x99, config.language[0]);
-     EXPECT_EQ(0xa4, config.language[1]);
+     EXPECT_EQ('\x99', config.language[0]);
+     EXPECT_EQ('\xA4', config.language[1]);
 
      char out[4] = { 1, 1, 1, 1};
      config.unpackLanguage(out);
diff --git a/libs/androidfw/tests/Split_test.cpp b/libs/androidfw/tests/Split_test.cpp
new file mode 100644
index 0000000..dbfdeae
--- /dev/null
+++ b/libs/androidfw/tests/Split_test.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+#include "TestHelpers.h"
+
+#include <gtest/gtest.h>
+
+/**
+ * Include a binary resource table. This table
+ * is a base table for an APK split.
+ *
+ * Package: com.android.example.split
+ *
+ * layout/main          0x7f020000 {default, fr-sw600dp-v13}
+ *
+ * string/app_title     0x7f030000 {default}
+ * string/test          0x7f030001 {default}
+ * string/boom          0x7f030002 {default}
+ * string/blah          0x7f030003 {default}
+ *
+ * array/lotsofstrings  0x7f040000 {default}
+ * array/numList        0x7f040001 {default}
+ * array/ary            0x7f040002 {default}
+ *
+ */
+#include "data/split_base_arsc.h"
+
+/**
+ * Include a binary resource table. This table
+ * is a configuration split table for an APK split.
+ *
+ * Package: com.android.example.split
+ *
+ * string/app_title     0x7f030000 {fr}
+ * string/test          0x7f030001 {de,fr}
+ * string/blah          0x7f030003 {fr}
+ *
+ * array/lotsofstrings  0x7f040000 {fr}
+ *
+ */
+#include "data/split_de_fr_arsc.h"
+
+
+using namespace android;
+
+enum { MAY_NOT_BE_BAG = false };
+
+void makeConfigFrench(ResTable_config* config) {
+    memset(config, 0, sizeof(*config));
+    config->language[0] = 'f';
+    config->language[1] = 'r';
+}
+
+TEST(SplitTest, TestLoadBase) {
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+}
+
+TEST(SplitTest, TestGetResourceFromBase) {
+    ResTable_config frenchConfig;
+    makeConfigFrench(&frenchConfig);
+
+    ResTable table;
+    table.setParameters(&frenchConfig);
+
+    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+
+    ResTable_config expectedConfig;
+    memset(&expectedConfig, 0, sizeof(expectedConfig));
+
+    Res_value val;
+    ResTable_config config;
+    ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
+
+    // The returned block should tell us which string pool to get the value, if it is a string.
+    EXPECT_GE(block, 0);
+
+    // We expect the default resource to be selected since it is the only resource configuration.
+    EXPECT_EQ(0, expectedConfig.compare(config));
+
+    EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+}
+
+TEST(SplitTest, TestGetResourceFromSplit) {
+    ResTable_config expectedConfig;
+    makeConfigFrench(&expectedConfig);
+
+    ResTable table;
+    table.setParameters(&expectedConfig);
+
+    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+    ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+    Res_value val;
+    ResTable_config config;
+    ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, NULL, &config);
+
+    EXPECT_GE(block, 0);
+
+    EXPECT_EQ(0, expectedConfig.compare(config));
+
+    EXPECT_EQ(Res_value::TYPE_STRING, val.dataType);
+}
+
+TEST(SplitTest, ResourcesFromBaseAndSplitHaveSameNames) {
+    ResTable_config expectedConfig;
+    makeConfigFrench(&expectedConfig);
+
+    ResTable table;
+    table.setParameters(&expectedConfig);
+
+    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+
+    ResTable::resource_name baseName;
+    EXPECT_TRUE(table.getResourceName(0x7f030003, false, &baseName));
+
+    ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+    ResTable::resource_name frName;
+    EXPECT_TRUE(table.getResourceName(0x7f030003, false, &frName));
+
+    EXPECT_EQ(
+            String16(baseName.package, baseName.packageLen),
+            String16(frName.package, frName.packageLen));
+
+    EXPECT_EQ(
+            String16(baseName.type, baseName.typeLen),
+            String16(frName.type, frName.typeLen));
+
+    EXPECT_EQ(
+            String16(baseName.name, baseName.nameLen),
+            String16(frName.name, frName.nameLen));
+}
+
+TEST(SplitTest, TypeEntrySpecFlagsAreUpdated) {
+    ResTable_config defaultConfig;
+    memset(&defaultConfig, 0, sizeof(defaultConfig));
+
+    ResTable table;
+    ASSERT_EQ(NO_ERROR, table.add(split_base_arsc, split_base_arsc_len));
+
+    Res_value val;
+    uint32_t specFlags = 0;
+    ssize_t block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &specFlags, NULL);
+    EXPECT_GE(block, 0);
+
+    EXPECT_EQ(static_cast<uint32_t>(0), specFlags);
+
+    ASSERT_EQ(NO_ERROR, table.add(split_de_fr_arsc, split_de_fr_arsc_len));
+
+    uint32_t frSpecFlags = 0;
+    block = table.getResource(0x7f030000, &val, MAY_NOT_BE_BAG, 0, &frSpecFlags, NULL);
+    EXPECT_GE(block, 0);
+
+    EXPECT_EQ(ResTable_config::CONFIG_LOCALE, frSpecFlags);
+}
diff --git a/libs/androidfw/tests/TestHelpers.h b/libs/androidfw/tests/TestHelpers.h
new file mode 100644
index 0000000..75a233a
--- /dev/null
+++ b/libs/androidfw/tests/TestHelpers.h
@@ -0,0 +1,17 @@
+#ifndef __TEST_HELPERS_H
+#define __TEST_HELPERS_H
+
+#include <ostream>
+
+#include <utils/String8.h>
+#include <utils/String16.h>
+
+static inline ::std::ostream& operator<<(::std::ostream& out, const android::String8& str) {
+    return out << str.string();
+}
+
+static inline ::std::ostream& operator<<(::std::ostream& out, const android::String16& str) {
+    return out << android::String8(str).string();
+}
+
+#endif // __TEST_HELPERS_H
diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp
new file mode 100644
index 0000000..d69abe5
--- /dev/null
+++ b/libs/androidfw/tests/TypeWrappers_test.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#include <androidfw/ResourceTypes.h>
+#include <androidfw/TypeWrappers.h>
+#include <utils/String8.h>
+
+#include <gtest/gtest.h>
+
+namespace android {
+
+void* createTypeData() {
+    ResTable_type t;
+    memset(&t, 0, sizeof(t));
+    t.header.type = RES_TABLE_TYPE_TYPE;
+    t.header.headerSize = sizeof(t);
+    t.id = 1;
+    t.entryCount = 3;
+
+    uint32_t offsets[3];
+    t.entriesStart = t.header.headerSize + sizeof(offsets);
+    t.header.size = t.entriesStart;
+
+    offsets[0] = 0;
+    ResTable_entry e1;
+    memset(&e1, 0, sizeof(e1));
+    e1.size = sizeof(e1);
+    e1.key.index = 0;
+    t.header.size += sizeof(e1);
+
+    Res_value v1;
+    memset(&v1, 0, sizeof(v1));
+    t.header.size += sizeof(v1);
+
+    offsets[1] = ResTable_type::NO_ENTRY;
+
+    offsets[2] = sizeof(e1) + sizeof(v1);
+    ResTable_entry e2;
+    memset(&e2, 0, sizeof(e2));
+    e2.size = sizeof(e2);
+    e2.key.index = 1;
+    t.header.size += sizeof(e2);
+
+    Res_value v2;
+    memset(&v2, 0, sizeof(v2));
+    t.header.size += sizeof(v2);
+
+    uint8_t* data = (uint8_t*)malloc(t.header.size);
+    uint8_t* p = data;
+    memcpy(p, &t, sizeof(t));
+    p += sizeof(t);
+    memcpy(p, offsets, sizeof(offsets));
+    p += sizeof(offsets);
+    memcpy(p, &e1, sizeof(e1));
+    p += sizeof(e1);
+    memcpy(p, &v1, sizeof(v1));
+    p += sizeof(v1);
+    memcpy(p, &e2, sizeof(e2));
+    p += sizeof(e2);
+    memcpy(p, &v2, sizeof(v2));
+    p += sizeof(v2);
+    return data;
+}
+
+TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
+    ResTable_type* data = (ResTable_type*) createTypeData();
+
+    TypeVariant v(data);
+
+    TypeVariant::iterator iter = v.beginEntries();
+    ASSERT_EQ(uint32_t(0), iter.index());
+    ASSERT_TRUE(NULL != *iter);
+    ASSERT_EQ(uint32_t(0), iter->key.index);
+    ASSERT_NE(v.endEntries(), iter);
+
+    iter++;
+
+    ASSERT_EQ(uint32_t(1), iter.index());
+    ASSERT_TRUE(NULL == *iter);
+    ASSERT_NE(v.endEntries(), iter);
+
+    iter++;
+
+    ASSERT_EQ(uint32_t(2), iter.index());
+    ASSERT_TRUE(NULL != *iter);
+    ASSERT_EQ(uint32_t(1), iter->key.index);
+    ASSERT_NE(v.endEntries(), iter);
+
+    iter++;
+
+    ASSERT_EQ(v.endEntries(), iter);
+
+    free(data);
+}
+
+} // namespace android
diff --git a/libs/androidfw/tests/data/.gitignore b/libs/androidfw/tests/data/.gitignore
new file mode 100644
index 0000000..c05cfb0
--- /dev/null
+++ b/libs/androidfw/tests/data/.gitignore
@@ -0,0 +1,2 @@
+*.apk
+*.arsc
diff --git a/libs/androidfw/tests/data/basic/AndroidManifest.xml b/libs/androidfw/tests/data/basic/AndroidManifest.xml
new file mode 100644
index 0000000..a56ac18
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test.basic">
+    <application>
+    </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/basic/basic_arsc.h b/libs/androidfw/tests/data/basic/basic_arsc.h
new file mode 100644
index 0000000..6532076
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/basic_arsc.h
@@ -0,0 +1,131 @@
+unsigned char basic_arsc[] = {
+  0x02, 0x00, 0x0c, 0x00, 0xfc, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x31, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+  0x32, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xb0, 0x05, 0x00, 0x00,
+  0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+  0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+  0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+  0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x9c, 0x01, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x7c, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00,
+  0x3c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00,
+  0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00,
+  0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00,
+  0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x73, 0x00, 0x74, 0x00, 0x79, 0x00, 0x6c, 0x00,
+  0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00,
+  0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+  0xdc, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
+  0x2a, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00,
+  0x5c, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x31, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00,
+  0x32, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
+  0x74, 0x00, 0x31, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00,
+  0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00,
+  0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00, 0x31, 0x00,
+  0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00,
+  0x65, 0x00, 0x72, 0x00, 0x32, 0x00, 0x00, 0x00, 0x06, 0x00, 0x54, 0x00,
+  0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x31, 0x00, 0x00, 0x00,
+  0x06, 0x00, 0x54, 0x00, 0x68, 0x00, 0x65, 0x00, 0x6d, 0x00, 0x65, 0x00,
+  0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00,
+  0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00, 0x72, 0x00,
+  0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x84, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x1c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+  0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x10, 0x05, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x18, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x10, 0xc8, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x00, 0x05, 0x7f,
+  0x01, 0x02, 0x44, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x73, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x10, 0x90, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x18, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x90, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x4c, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x10,
+  0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x7f, 0x08, 0x00, 0x00, 0x01,
+  0x00, 0x00, 0x03, 0x7f, 0x10, 0x00, 0x01, 0x00, 0x07, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x04, 0x7f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x7f,
+  0x08, 0x00, 0x00, 0x10, 0x2c, 0x01, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x7c, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+  0x08, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02,
+  0x08, 0x00, 0x00, 0x10, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x02,
+  0x08, 0x00, 0x00, 0x10, 0x03, 0x00, 0x00, 0x00
+};
+unsigned int basic_arsc_len = 1532;
diff --git a/libs/androidfw/tests/data/basic/build b/libs/androidfw/tests/data/basic/build
new file mode 100755
index 0000000..237342c
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc basic.arsc && \
+xxd -i basic.arsc > basic_arsc.h
diff --git a/libs/androidfw/tests/data/basic/res/values-sv/values.xml b/libs/androidfw/tests/data/basic/res/values-sv/values.xml
new file mode 100644
index 0000000..9d52307
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values-sv/values.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <integer name="number1">400</integer>
+</resources>
diff --git a/libs/androidfw/tests/data/basic/res/values/values.xml b/libs/androidfw/tests/data/basic/res/values/values.xml
new file mode 100644
index 0000000..662eda6
--- /dev/null
+++ b/libs/androidfw/tests/data/basic/res/values/values.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <attr name="attr1" format="reference|integer" />
+    <attr name="attr2" format="reference|integer" />
+
+    <string name="test1">test1</string>
+    <string name="test2">test2</string>
+
+    <integer name="number1">200</integer>
+    <integer name="number2">@array/integerArray1</integer>
+
+    <style name="Theme1">
+        <item name="com.android.test.basic:attr1">100</item>
+        <item name="com.android.test.basic:attr2">@integer/number1</item>
+    </style>
+
+    <style name="Theme2" parent="@com.android.test.basic:style/Theme1">
+        <item name="com.android.test.basic:attr1">300</item>
+    </style>
+
+    <integer-array name="integerArray1">
+        <item>1</item>
+        <item>2</item>
+        <item>3</item>
+    </integer-array>
+</resources>
diff --git a/libs/androidfw/tests/data/overlay/AndroidManifest.xml b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
new file mode 100644
index 0000000..a56ac18
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.test.basic">
+    <application>
+    </application>
+</manifest>
diff --git a/libs/androidfw/tests/data/overlay/build b/libs/androidfw/tests/data/overlay/build
new file mode 100755
index 0000000..87cf6de
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/build
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+aapt package -M AndroidManifest.xml -S res -F bundle.apk -f && \
+unzip bundle.apk resources.arsc && \
+mv resources.arsc overlay.arsc && \
+xxd -i overlay.arsc > overlay_arsc.h
diff --git a/libs/androidfw/tests/data/overlay/overlay_arsc.h b/libs/androidfw/tests/data/overlay/overlay_arsc.h
new file mode 100644
index 0000000..5bd98b2
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/overlay_arsc.h
@@ -0,0 +1,69 @@
+unsigned char overlay_arsc[] = {
+  0x02, 0x00, 0x0c, 0x00, 0x10, 0x03, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x74, 0x00,
+  0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x6f, 0x00,
+  0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x20, 0x01, 0xc4, 0x02, 0x00, 0x00,
+  0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+  0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+  0x64, 0x00, 0x2e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+  0x2e, 0x00, 0x62, 0x00, 0x61, 0x00, 0x73, 0x00, 0x69, 0x00, 0x63, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x01, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x74, 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x54, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
+  0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00,
+  0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x50, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00,
+  0x74, 0x00, 0x32, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x69, 0x00, 0x6e, 0x00,
+  0x74, 0x00, 0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00,
+  0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x31, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x70, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+  0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x10,
+  0x0b, 0x00, 0x00, 0x00
+};
+unsigned int overlay_arsc_len = 784;
diff --git a/libs/androidfw/tests/data/overlay/res/values/values.xml b/libs/androidfw/tests/data/overlay/res/values/values.xml
new file mode 100644
index 0000000..227e889
--- /dev/null
+++ b/libs/androidfw/tests/data/overlay/res/values/values.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="test2">test2-overlay</string>
+    <integer-array name="integerArray1">
+        <item>10</item>
+        <item>11</item>
+    </integer-array>
+</resources>
diff --git a/libs/androidfw/tests/data/split_base_arsc.h b/libs/androidfw/tests/data/split_base_arsc.h
new file mode 100644
index 0000000..e0321e9
--- /dev/null
+++ b/libs/androidfw/tests/data/split_base_arsc.h
@@ -0,0 +1,221 @@
+unsigned char split_base_arsc[] = {
+  0x02, 0x00, 0x0c, 0x00, 0x30, 0x0a, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0x94, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2a, 0x00, 0x00, 0x00,
+  0x72, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x00, 0x00,
+  0xb4, 0x00, 0x00, 0x00, 0xc6, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00,
+  0xf4, 0x00, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x2a, 0x01, 0x00, 0x00,
+  0x44, 0x01, 0x00, 0x00, 0x13, 0x00, 0x72, 0x00, 0x65, 0x00, 0x73, 0x00,
+  0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00,
+  0x74, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00,
+  0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x22, 0x00,
+  0x72, 0x00, 0x65, 0x00, 0x73, 0x00, 0x2f, 0x00, 0x6c, 0x00, 0x61, 0x00,
+  0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x2d, 0x00, 0x66, 0x00,
+  0x72, 0x00, 0x2d, 0x00, 0x73, 0x00, 0x77, 0x00, 0x36, 0x00, 0x30, 0x00,
+  0x30, 0x00, 0x64, 0x00, 0x70, 0x00, 0x2d, 0x00, 0x76, 0x00, 0x31, 0x00,
+  0x33, 0x00, 0x2f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00,
+  0x2e, 0x00, 0x78, 0x00, 0x6d, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x09, 0x00,
+  0x53, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x20, 0x00,
+  0x41, 0x00, 0x50, 0x00, 0x4b, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x48, 0x00,
+  0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x2c, 0x00, 0x20, 0x00,
+  0x57, 0x00, 0x6f, 0x00, 0x72, 0x00, 0x6c, 0x00, 0x64, 0x00, 0x21, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6d, 0x00,
+  0x21, 0x00, 0x00, 0x00, 0x07, 0x00, 0x42, 0x00, 0x6c, 0x00, 0x61, 0x00,
+  0x68, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x0b, 0x00,
+  0x48, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x20, 0x00,
+  0x74, 0x00, 0x68, 0x00, 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x47, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x64, 0x00, 0x20, 0x00,
+  0x62, 0x00, 0x79, 0x00, 0x65, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x49, 0x00,
+  0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x41, 0x00,
+  0x4c, 0x00, 0x4c, 0x00, 0x21, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x49, 0x00,
+  0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x31, 0x00,
+  0x21, 0x00, 0x31, 0x00, 0x21, 0x00, 0x20, 0x00, 0x3a, 0x00, 0x29, 0x00,
+  0x00, 0x00, 0x0b, 0x00, 0x49, 0x00, 0x20, 0x00, 0x6e, 0x00, 0x6f, 0x00,
+  0x20, 0x00, 0x68, 0x00, 0x61, 0x00, 0x7a, 0x00, 0x20, 0x00, 0x3a, 0x00,
+  0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x01,
+  0x90, 0x08, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00,
+  0x6d, 0x00, 0x2e, 0x00, 0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00,
+  0x6f, 0x00, 0x69, 0x00, 0x64, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00,
+  0x61, 0x00, 0x6d, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2e, 0x00,
+  0x73, 0x00, 0x70, 0x00, 0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x1c, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00,
+  0x0e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00, 0xd4, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x0c, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00,
+  0x3a, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00,
+  0x66, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x61, 0x00, 0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00,
+  0x06, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00,
+  0x74, 0x00, 0x00, 0x00, 0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00,
+  0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00,
+  0x72, 0x00, 0x72, 0x00, 0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x07, 0x00,
+  0x70, 0x00, 0x6c, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6c, 0x00,
+  0x73, 0x00, 0x00, 0x00, 0x04, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00,
+  0x6c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6c, 0x00,
+  0x6f, 0x00, 0x72, 0x00, 0x00, 0x00, 0x05, 0x00, 0x64, 0x00, 0x69, 0x00,
+  0x6d, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x69, 0x00,
+  0x64, 0x00, 0x00, 0x00, 0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00,
+  0x65, 0x00, 0x67, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0x30, 0x01, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+  0x22, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+  0x46, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x76, 0x00, 0x00, 0x00,
+  0x80, 0x00, 0x00, 0x00, 0x8c, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00,
+  0xa4, 0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0xca, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x74, 0x00,
+  0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x00, 0x00, 0x04, 0x00,
+  0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x04, 0x00,
+  0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x04, 0x00,
+  0x62, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x68, 0x00, 0x00, 0x00, 0x0d, 0x00,
+  0x6c, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x73, 0x00, 0x6f, 0x00, 0x66, 0x00,
+  0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00,
+  0x73, 0x00, 0x00, 0x00, 0x07, 0x00, 0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00,
+  0x4c, 0x00, 0x69, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x03, 0x00,
+  0x61, 0x00, 0x72, 0x00, 0x79, 0x00, 0x00, 0x00, 0x04, 0x00, 0x70, 0x00,
+  0x6c, 0x00, 0x75, 0x00, 0x72, 0x00, 0x00, 0x00, 0x03, 0x00, 0x71, 0x00,
+  0x75, 0x00, 0x65, 0x00, 0x00, 0x00, 0x05, 0x00, 0x67, 0x00, 0x72, 0x00,
+  0x65, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x77, 0x00,
+  0x69, 0x00, 0x64, 0x00, 0x74, 0x00, 0x68, 0x00, 0x00, 0x00, 0x0a, 0x00,
+  0x69, 0x00, 0x64, 0x00, 0x65, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x69, 0x00,
+  0x66, 0x00, 0x69, 0x00, 0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00,
+  0x6e, 0x00, 0x75, 0x00, 0x6d, 0x00, 0x62, 0x00, 0x65, 0x00, 0x72, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x10, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x04, 0x24, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x58, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x02,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x94, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x54, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+  0x20, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x02, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+  0x03, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0xc8, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+  0x10, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03,
+  0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03,
+  0x07, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02,
+  0x08, 0x00, 0x00, 0x10, 0xd2, 0x04, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+  0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x01, 0x00, 0x03, 0x7f,
+  0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x01, 0x02, 0x00, 0x03, 0x7f,
+  0x02, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x05, 0x01, 0x19, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x7c, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x01, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x0a, 0x00, 0x00, 0x00,
+  0x06, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x09, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x00, 0x01, 0x08, 0x00, 0x00, 0x03, 0x08, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x58, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x12, 0xff, 0xff, 0xff, 0xff,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x58, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x1d, 0x00, 0xff, 0x00, 0xff,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x0b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05, 0x01, 0x17, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x58, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x05,
+  0x01, 0xe6, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+  0x0b, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x58, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x10,
+  0x7b, 0x00, 0x00, 0x00
+};
+unsigned int split_base_arsc_len = 2608;
diff --git a/libs/androidfw/tests/data/split_de_fr_arsc.h b/libs/androidfw/tests/data/split_de_fr_arsc.h
new file mode 100644
index 0000000..6f6a416
--- /dev/null
+++ b/libs/androidfw/tests/data/split_de_fr_arsc.h
@@ -0,0 +1,118 @@
+unsigned char split_de_fr_arsc[] = {
+  0x02, 0x00, 0x0c, 0x00, 0x64, 0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x2c, 0x00, 0x00, 0x00, 0x4e, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x00, 0x00,
+  0x6c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x41, 0x00, 0x63, 0x00, 0x68, 0x00,
+  0x74, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x21, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x41, 0x00, 0x50, 0x00, 0x4b, 0x00, 0x20, 0x00, 0x44, 0x00,
+  0x69, 0x00, 0x76, 0x00, 0x69, 0x00, 0x73, 0x00, 0xe9, 0x00, 0x00, 0x00,
+  0x0f, 0x00, 0x42, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6a, 0x00, 0x6f, 0x00,
+  0x75, 0x00, 0x72, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x4d, 0x00, 0x6f, 0x00,
+  0x6e, 0x00, 0x64, 0x00, 0x65, 0x00, 0x21, 0x00, 0x00, 0x00, 0x06, 0x00,
+  0x42, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x68, 0x00, 0x2e, 0x00, 0x2e, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x48, 0x00, 0xe9, 0x00, 0x20, 0x00, 0x6c, 0x00,
+  0xe0, 0x00, 0x00, 0x00, 0x09, 0x00, 0x41, 0x00, 0x75, 0x00, 0x20, 0x00,
+  0x72, 0x00, 0x65, 0x00, 0x76, 0x00, 0x6f, 0x00, 0x69, 0x00, 0x72, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x1c, 0x01, 0xa0, 0x04, 0x00, 0x00,
+  0x7f, 0x00, 0x00, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x2e, 0x00,
+  0x61, 0x00, 0x6e, 0x00, 0x64, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x69, 0x00,
+  0x64, 0x00, 0x2e, 0x00, 0x65, 0x00, 0x78, 0x00, 0x61, 0x00, 0x6d, 0x00,
+  0x70, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x2e, 0x00, 0x73, 0x00, 0x70, 0x00,
+  0x6c, 0x00, 0x69, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x01, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0xf0, 0x01, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x1c, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
+  0x1c, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00,
+  0x4c, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x66, 0x00, 0x00, 0x00,
+  0x74, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x61, 0x00,
+  0x74, 0x00, 0x74, 0x00, 0x72, 0x00, 0x00, 0x00, 0x06, 0x00, 0x6c, 0x00,
+  0x61, 0x00, 0x79, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x74, 0x00, 0x00, 0x00,
+  0x06, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00,
+  0x67, 0x00, 0x00, 0x00, 0x05, 0x00, 0x61, 0x00, 0x72, 0x00, 0x72, 0x00,
+  0x61, 0x00, 0x79, 0x00, 0x00, 0x00, 0x07, 0x00, 0x70, 0x00, 0x6c, 0x00,
+  0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x73, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x62, 0x00, 0x6f, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x00, 0x00,
+  0x05, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x72, 0x00,
+  0x00, 0x00, 0x05, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x65, 0x00,
+  0x6e, 0x00, 0x00, 0x00, 0x02, 0x00, 0x69, 0x00, 0x64, 0x00, 0x00, 0x00,
+  0x07, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x67, 0x00,
+  0x65, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x1c, 0x00,
+  0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00,
+  0x2e, 0x00, 0x00, 0x00, 0x09, 0x00, 0x61, 0x00, 0x70, 0x00, 0x70, 0x00,
+  0x5f, 0x00, 0x74, 0x00, 0x69, 0x00, 0x74, 0x00, 0x6c, 0x00, 0x65, 0x00,
+  0x00, 0x00, 0x04, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, 0x74, 0x00,
+  0x00, 0x00, 0x04, 0x00, 0x62, 0x00, 0x6c, 0x00, 0x61, 0x00, 0x68, 0x00,
+  0x00, 0x00, 0x0d, 0x00, 0x6c, 0x00, 0x6f, 0x00, 0x74, 0x00, 0x73, 0x00,
+  0x6f, 0x00, 0x66, 0x00, 0x73, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00,
+  0x6e, 0x00, 0x67, 0x00, 0x73, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x20, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00, 0x64, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00,
+  0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+  0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x44, 0x00,
+  0x84, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x54, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0x20, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03, 0x01, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x03,
+  0x02, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x08, 0x00, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x1c, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+  0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x01, 0x02, 0x44, 0x00, 0x78, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x66, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x01, 0x00,
+  0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x04, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x03, 0x05, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x07, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00,
+  0x14, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x02, 0x02, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00,
+  0x0a, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+unsigned int split_de_fr_arsc_len = 1380;
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 02e85fe..a704e19 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -45,6 +45,7 @@
 		RenderBufferCache.cpp \
 		RenderNode.cpp \
 		RenderProperties.cpp \
+		RenderState.cpp \
 		ResourceCache.cpp \
 		ShadowTessellator.cpp \
 		SkiaShader.cpp \
@@ -61,6 +62,7 @@
 	LOCAL_SRC_FILES += \
 		renderthread/CanvasContext.cpp \
 		renderthread/DrawFrameTask.cpp \
+		renderthread/EglManager.cpp \
 		renderthread/RenderProxy.cpp \
 		renderthread/RenderTask.cpp \
 		renderthread/RenderThread.cpp \
@@ -69,8 +71,6 @@
 	intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,TARGET,)
 
 	LOCAL_C_INCLUDES += \
-		$(JNI_H_INCLUDE) \
-		$(LOCAL_PATH)/../../include/utils \
 		external/skia/src/core
 
 	LOCAL_CFLAGS += -DUSE_OPENGL_RENDERER -DEGL_EGLEXT_PROTOTYPES -DGL_GLEXT_PROTOTYPES
diff --git a/libs/hwui/Animator.cpp b/libs/hwui/Animator.cpp
index eff3011..dc6d852 100644
--- a/libs/hwui/Animator.cpp
+++ b/libs/hwui/Animator.cpp
@@ -160,6 +160,10 @@
     (target->mutateStagingProperties().*mPropertyAccess->setter)(finalValue());
 }
 
+uint32_t RenderPropertyAnimator::dirtyMask() {
+    return mPropertyAccess->dirtyMask;
+}
+
 float RenderPropertyAnimator::getValue(RenderNode* target) const {
     return (target->properties().*mPropertyAccess->getter)();
 }
diff --git a/libs/hwui/Animator.h b/libs/hwui/Animator.h
index 203cdff..6cb72c4c 100644
--- a/libs/hwui/Animator.h
+++ b/libs/hwui/Animator.h
@@ -61,6 +61,8 @@
     bool isFinished() { return mPlayState == FINISHED; }
     float finalValue() { return mFinalValue; }
 
+    ANDROID_API virtual uint32_t dirtyMask() { return 0; }
+
 protected:
     BaseRenderNodeAnimator(float finalValue);
     virtual ~BaseRenderNodeAnimator();
@@ -112,6 +114,8 @@
 
     ANDROID_API virtual void onAttached(RenderNode* target);
 
+    ANDROID_API virtual uint32_t dirtyMask();
+
 protected:
     virtual float getValue(RenderNode* target) const;
     virtual void setValue(RenderNode* target, float value);
diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp
index 77d16ab..8b32c40 100644
--- a/libs/hwui/DamageAccumulator.cpp
+++ b/libs/hwui/DamageAccumulator.cpp
@@ -170,6 +170,9 @@
     }
 
     const RenderProperties& props = frame->renderNode->properties();
+    if (props.getAlpha() <= 0) {
+        return;
+    }
 
     // Perform clipping
     if (props.getClipDamageToBounds() && !frame->pendingDirty.isEmpty()) {
diff --git a/libs/hwui/DeferredDisplayList.cpp b/libs/hwui/DeferredDisplayList.cpp
index 937bf8d..a998594 100644
--- a/libs/hwui/DeferredDisplayList.cpp
+++ b/libs/hwui/DeferredDisplayList.cpp
@@ -500,7 +500,7 @@
 void DeferredDisplayList::addDrawOp(OpenGLRenderer& renderer, DrawOp* op) {
     /* 1: op calculates local bounds */
     DeferredDisplayState* const state = createState();
-    if (op->getLocalBounds(renderer.getDrawModifiers(), state->mBounds)) {
+    if (op->getLocalBounds(state->mBounds)) {
         if (state->mBounds.isEmpty()) {
             // valid empty bounds, don't bother deferring
             tryRecycleState(state);
diff --git a/libs/hwui/DeferredDisplayList.h b/libs/hwui/DeferredDisplayList.h
index 48489c2..8a015b21 100644
--- a/libs/hwui/DeferredDisplayList.h
+++ b/libs/hwui/DeferredDisplayList.h
@@ -38,7 +38,6 @@
 class StateOp;
 
 class DeferredDisplayState;
-class OpenGLRenderer;
 
 class Batch;
 class DrawBatch;
diff --git a/libs/hwui/DeferredLayerUpdater.h b/libs/hwui/DeferredLayerUpdater.h
index 5082271..c76bd5e 100644
--- a/libs/hwui/DeferredLayerUpdater.h
+++ b/libs/hwui/DeferredLayerUpdater.h
@@ -23,7 +23,6 @@
 #include <utils/StrongPointer.h>
 
 #include "Layer.h"
-#include "OpenGLRenderer.h"
 #include "Rect.h"
 #include "RenderNode.h"
 
diff --git a/libs/hwui/DisplayList.cpp b/libs/hwui/DisplayList.cpp
index f418c9b..e38b532 100644
--- a/libs/hwui/DisplayList.cpp
+++ b/libs/hwui/DisplayList.cpp
@@ -90,8 +90,8 @@
     layers.clear();
 }
 
-void DisplayListData::addChild(DrawDisplayListOp* op) {
-    LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawDisplayListOp with no render node!");
+void DisplayListData::addChild(DrawRenderNodeOp* op) {
+    LOG_ALWAYS_FATAL_IF(!op->renderNode(), "DrawRenderNodeOp with no render node!");
 
     mChildren.push(op);
     mReferenceHolders.push(op->renderNode());
diff --git a/libs/hwui/DisplayList.h b/libs/hwui/DisplayList.h
index 7b7dc16..bfffbb4 100644
--- a/libs/hwui/DisplayList.h
+++ b/libs/hwui/DisplayList.h
@@ -61,7 +61,7 @@
 class SaveLayerOp;
 class SaveOp;
 class RestoreToCountOp;
-class DrawDisplayListOp;
+class DrawRenderNodeOp;
 
 /**
  * Holds data used in the playback a tree of DisplayLists.
@@ -133,8 +133,8 @@
         return !displayListOps.size();
     }
 
-    void addChild(DrawDisplayListOp* childOp);
-    const Vector<DrawDisplayListOp*>& children() { return mChildren; }
+    void addChild(DrawRenderNodeOp* childOp);
+    const Vector<DrawRenderNodeOp*>& children() { return mChildren; }
 
     void refProperty(CanvasPropertyPrimitive* prop) {
         mReferenceHolders.push(prop);
@@ -148,7 +148,7 @@
     Vector< sp<VirtualLightRefBase> > mReferenceHolders;
 
     // list of children display lists for quick, non-drawing traversal
-    Vector<DrawDisplayListOp*> mChildren;
+    Vector<DrawRenderNodeOp*> mChildren;
 
     void cleanupResources();
 };
diff --git a/libs/hwui/DisplayListOp.h b/libs/hwui/DisplayListOp.h
index 233f3f0..5ed04a0 100644
--- a/libs/hwui/DisplayListOp.h
+++ b/libs/hwui/DisplayListOp.h
@@ -171,7 +171,7 @@
      *
      * returns true if bounds exist
      */
-    virtual bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
+    virtual bool getLocalBounds(Rect& localBounds) {
         return false;
     }
 
@@ -253,7 +253,7 @@
     // default empty constructor for bounds, to be overridden in child constructor body
     DrawBoundedOp(const SkPaint* paint): DrawOp(paint) { }
 
-    bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
+    bool getLocalBounds(Rect& localBounds) {
         localBounds.set(mLocalBounds);
         OpenGLRenderer::TextShadow textShadow;
         if (OpenGLRenderer::getTextShadow(mPaint, &textShadow)) {
@@ -1029,7 +1029,7 @@
     DrawStrokableOp(float left, float top, float right, float bottom, const SkPaint* paint)
             : DrawBoundedOp(left, top, right, bottom, paint) {};
 
-    bool getLocalBounds(const DrawModifiers& drawModifiers, Rect& localBounds) {
+    bool getLocalBounds(Rect& localBounds) {
         localBounds.set(mLocalBounds);
         if (mPaint && mPaint->getStyle() != SkPaint::kFill_Style) {
             localBounds.outset(strokeWidthOutset());
@@ -1118,8 +1118,8 @@
             const DeferredDisplayState& state) {
         DrawStrokableOp::onDefer(renderer, deferInfo, state);
         if (!mPaint->getPathEffect()) {
-            renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix,
-                    mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy, mPaint);
+            renderer.getCaches().tessellationCache.precacheRoundRect(state.mMatrix, *mPaint,
+                    mLocalBounds.getWidth(), mLocalBounds.getHeight(), mRx, mRy);
         }
     }
 
@@ -1399,7 +1399,7 @@
 
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
         Rect bounds;
-        getLocalBounds(renderer.getDrawModifiers(), bounds);
+        getLocalBounds(bounds);
         return renderer.drawText(mText, mBytesCount, mCount, mX, mY,
                 mPositions, getPaint(renderer), mTotalAdvance, bounds);
     }
@@ -1465,48 +1465,48 @@
     Functor* mFunctor;
 };
 
-class DrawDisplayListOp : public DrawBoundedOp {
-    friend class RenderNode; // grant DisplayList access to info of child
+class DrawRenderNodeOp : public DrawBoundedOp {
+    friend class RenderNode; // grant RenderNode access to info of child
 public:
-    DrawDisplayListOp(RenderNode* displayList, int flags, const mat4& transformFromParent)
-            : DrawBoundedOp(0, 0, displayList->getWidth(), displayList->getHeight(), 0),
-            mDisplayList(displayList), mFlags(flags), mTransformFromParent(transformFromParent) {}
+    DrawRenderNodeOp(RenderNode* renderNode, int flags, const mat4& transformFromParent)
+            : DrawBoundedOp(0, 0, renderNode->getWidth(), renderNode->getHeight(), 0),
+            mRenderNode(renderNode), mFlags(flags), mTransformFromParent(transformFromParent) {}
 
     virtual void defer(DeferStateStruct& deferStruct, int saveCount, int level,
             bool useQuickReject) {
-        if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) {
-            mDisplayList->deferNodeInParent(deferStruct, level + 1);
+        if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) {
+            mRenderNode->defer(deferStruct, level + 1);
         }
     }
     virtual void replay(ReplayStateStruct& replayStruct, int saveCount, int level,
             bool useQuickReject) {
-        if (mDisplayList && mDisplayList->isRenderable() && !mSkipInOrderDraw) {
-            mDisplayList->replayNodeInParent(replayStruct, level + 1);
+        if (mRenderNode && mRenderNode->isRenderable() && !mSkipInOrderDraw) {
+            mRenderNode->replay(replayStruct, level + 1);
         }
     }
 
-    // NOT USED since replay() is overridden
     virtual status_t applyDraw(OpenGLRenderer& renderer, Rect& dirty) {
-        return DrawGlInfo::kStatusDone;
+        LOG_ALWAYS_FATAL("should not be called, because replay() is overridden");
+        return 0;
     }
 
     virtual void output(int level, uint32_t logFlags) const {
-        OP_LOG("Draw Display List %p, flags %#x", mDisplayList, mFlags);
-        if (mDisplayList && (logFlags & kOpLogFlag_Recurse)) {
-            mDisplayList->output(level + 1);
+        OP_LOG("Draw Display List %p, flags %#x", mRenderNode, mFlags);
+        if (mRenderNode && (logFlags & kOpLogFlag_Recurse)) {
+            mRenderNode->output(level + 1);
         }
     }
 
-    virtual const char* name() { return "DrawDisplayList"; }
+    virtual const char* name() { return "DrawRenderNode"; }
 
-    RenderNode* renderNode() { return mDisplayList; }
+    RenderNode* renderNode() { return mRenderNode; }
 
 private:
-    RenderNode* mDisplayList;
+    RenderNode* mRenderNode;
     const int mFlags;
 
     ///////////////////////////
-    // Properties below are used by DisplayList::computeOrderingImpl() and iterate()
+    // Properties below are used by RenderNode::computeOrderingImpl() and issueOperations()
     ///////////////////////////
     /**
      * Records transform vs parent, used for computing total transform without rerunning DL contents
@@ -1514,12 +1514,12 @@
     const mat4 mTransformFromParent;
 
     /**
-     * Holds the transformation between the projection surface ViewGroup and this DisplayList
+     * Holds the transformation between the projection surface ViewGroup and this RenderNode
      * drawing instance. Represents any translations / transformations done within the drawing of
      * the compositing ancestor ViewGroup's draw, before the draw of the View represented by this
      * DisplayList draw instance.
      *
-     * Note: doesn't include any transformation recorded within the DisplayList and its properties.
+     * Note: doesn't include transformation within the RenderNode, or its properties.
      */
     mat4 mTransformFromCompositingAncestor;
     bool mSkipInOrderDraw;
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index 0e47c6e2..7789358 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -169,18 +169,16 @@
     return StatefulBaseRenderer::clipRegion(region, op);
 }
 
-status_t DisplayListRenderer::drawDisplayList(RenderNode* displayList,
-        Rect& dirty, int32_t flags) {
+status_t DisplayListRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t flags) {
     // dirty is an out parameter and should not be recorded,
     // it matters only when replaying the display list
 
-    if (displayList->stagingProperties().isProjectionReceiver()) {
+    if (renderNode->stagingProperties().isProjectionReceiver()) {
         // use staging property, since recording on UI thread
         mDisplayListData->projectionReceiveIndex = mDisplayListData->displayListOps.size();
     }
 
-    DrawDisplayListOp* op = new (alloc()) DrawDisplayListOp(displayList,
-            flags, *currentTransform());
+    DrawRenderNodeOp* op = new (alloc()) DrawRenderNodeOp(renderNode, flags, *currentTransform());
     addDrawOp(op);
     mDisplayListData->addChild(op);
     return DrawGlInfo::kStatusDone;
@@ -308,6 +306,10 @@
 
 status_t DisplayListRenderer::drawArc(float left, float top, float right, float bottom,
         float startAngle, float sweepAngle, bool useCenter, const SkPaint* paint) {
+    if (fabs(sweepAngle) > 360.0f) {
+        return drawOval(left, top, right, bottom, paint);
+    }
+
     paint = refPaint(paint);
     addDrawOp(new (alloc()) DrawArcOp(left, top, right, bottom,
                     startAngle, sweepAngle, useCenter, paint));
@@ -423,7 +425,7 @@
 
 void DisplayListRenderer::addDrawOp(DrawOp* op) {
     Rect localBounds;
-    if (op->getLocalBounds(mDrawModifiers, localBounds)) {
+    if (op->getLocalBounds(localBounds)) {
         bool rejected = quickRejectConservative(localBounds.left, localBounds.top,
                 localBounds.right, localBounds.bottom);
         op->setQuickRejected(rejected);
diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h
index 2eaa671..8ca9af1 100644
--- a/libs/hwui/DisplayListRenderer.h
+++ b/libs/hwui/DisplayListRenderer.h
@@ -23,7 +23,6 @@
 #include <cutils/compiler.h>
 
 #include "DisplayListLogBuffer.h"
-#include "OpenGLRenderer.h"
 #include "RenderNode.h"
 
 namespace android {
@@ -53,12 +52,12 @@
 /**
  * Records drawing commands in a display list for later playback into an OpenGLRenderer.
  */
-class DisplayListRenderer: public OpenGLRenderer {
+class ANDROID_API DisplayListRenderer: public StatefulBaseRenderer {
 public:
-    ANDROID_API DisplayListRenderer();
+    DisplayListRenderer();
     virtual ~DisplayListRenderer();
 
-    ANDROID_API DisplayListData* finishRecording();
+    DisplayListData* finishRecording();
 
     virtual bool isRecording() const { return true; }
 
@@ -81,7 +80,7 @@
             const SkPaint* paint, int flags);
 
     // Matrix
-    virtual void translate(float dx, float dy, float dz);
+    virtual void translate(float dx, float dy, float dz = 0.0f);
     virtual void rotate(float degrees);
     virtual void scale(float sx, float sy);
     virtual void skew(float sx, float sy);
@@ -98,6 +97,10 @@
     virtual void resetPaintFilter();
     virtual void setupPaintFilter(int clearBits, int setBits);
 
+    bool isCurrentTransformSimple() {
+        return currentTransform()->isSimple();
+    }
+
 // ----------------------------------------------------------------------------
 // Canvas draw operations
 // ----------------------------------------------------------------------------
@@ -148,16 +151,10 @@
 // Canvas draw operations - special
 // ----------------------------------------------------------------------------
     virtual status_t drawLayer(Layer* layer, float x, float y);
-    virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty,
-            int32_t replayFlags);
+    virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags);
 
     // TODO: rename for consistency
     virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
-protected:
-    // NOTE: must override these to avoid calling into super class, which calls GL. These may be
-    // removed once DisplayListRenderer no longer inherits from OpenGLRenderer
-    virtual void onViewportInitialized() {};
-    virtual void onSnapshotRestored() {};
 
 private:
     void insertRestoreToCount();
diff --git a/libs/hwui/Interpolator.cpp b/libs/hwui/Interpolator.cpp
index 1f84b86..fc0e8a0 100644
--- a/libs/hwui/Interpolator.cpp
+++ b/libs/hwui/Interpolator.cpp
@@ -112,6 +112,10 @@
     int i1 = (int) ipart;
     int i2 = MathUtils::min(i1 + 1, mSize - 1);
 
+    LOG_ALWAYS_FATAL_IF(i1 < 0 || i2 < 0, "negatives in interpolation!"
+            " i1=%d, i2=%d, input=%f, lutpos=%f, size=%zu, values=%p, ipart=%f, weight=%f",
+            i1, i2, input, lutpos, mSize, mValues, ipart, weight);
+
     float v1 = mValues[i1];
     float v2 = mValues[i2];
 
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index 1002e13..73a4da5 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -20,6 +20,7 @@
 
 #include "Caches.h"
 #include "DeferredDisplayList.h"
+#include "RenderState.h"
 #include "Layer.h"
 #include "LayerRenderer.h"
 #include "OpenGLRenderer.h"
@@ -28,8 +29,10 @@
 namespace android {
 namespace uirenderer {
 
-Layer::Layer(const uint32_t layerWidth, const uint32_t layerHeight):
-        caches(Caches::getInstance()), texture(caches) {
+Layer::Layer(RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight)
+        : caches(Caches::getInstance())
+        , renderState(renderState)
+        , texture(caches) {
     mesh = NULL;
     meshElementCount = 0;
     cacheable = true;
@@ -41,7 +44,7 @@
     colorFilter = NULL;
     deferredUpdateScheduled = false;
     renderer = NULL;
-    displayList = NULL;
+    renderNode = NULL;
     fbo = 0;
     stencil = NULL;
     debugDrawUpdate = false;
@@ -72,7 +75,7 @@
 
 void Layer::requireRenderer() {
     if (!renderer) {
-        renderer = new LayerRenderer(this);
+        renderer = new LayerRenderer(renderState, this);
         renderer->initProperties();
     }
 }
@@ -123,28 +126,26 @@
 
 void Layer::removeFbo(bool flush) {
     if (stencil) {
-        GLuint previousFbo;
-        glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
-        if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+        GLuint previousFbo = renderState.getFramebuffer();
+        renderState.bindFramebuffer(fbo);
         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
-        if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+        renderState.bindFramebuffer(previousFbo);
 
         caches.renderBufferCache.put(stencil);
         stencil = NULL;
     }
 
     if (fbo) {
-        if (flush) LayerRenderer::flushLayer(this);
+        if (flush) LayerRenderer::flushLayer(renderState, this);
         // If put fails the cache will delete the FBO
         caches.fboCache.put(fbo);
         fbo = 0;
     }
 }
 
-void Layer::updateDeferred(RenderNode* displayList,
-        int left, int top, int right, int bottom) {
+void Layer::updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom) {
     requireRenderer();
-    this->displayList = displayList;
+    this->renderNode = renderNode;
     const Rect r(left, top, right, bottom);
     dirtyRect.unionWith(r);
     deferredUpdateScheduled = true;
@@ -219,14 +220,14 @@
     renderer->setupFrameState(dirtyRect.left, dirtyRect.top,
             dirtyRect.right, dirtyRect.bottom, !isBlend());
 
-    displayList->computeOrdering();
-    displayList->deferNodeTree(deferredState);
+    renderNode->computeOrdering();
+    renderNode->defer(deferredState, 0);
 
     deferredUpdateScheduled = false;
 }
 
 void Layer::cancelDefer() {
-    displayList = NULL;
+    renderNode = NULL;
     deferredUpdateScheduled = false;
     if (deferredList) {
         delete deferredList;
@@ -246,7 +247,7 @@
         renderer->finish();
 
         dirtyRect.setEmpty();
-        displayList = NULL;
+        renderNode = NULL;
     }
 }
 
@@ -255,14 +256,14 @@
     renderer->prepareDirty(dirtyRect.left, dirtyRect.top, dirtyRect.right, dirtyRect.bottom,
             !isBlend());
 
-    renderer->drawDisplayList(displayList.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren);
+    renderer->drawRenderNode(renderNode.get(), dirtyRect, RenderNode::kReplayFlag_ClipChildren);
 
     renderer->finish();
 
     dirtyRect.setEmpty();
 
     deferredUpdateScheduled = false;
-    displayList = NULL;
+    renderNode = NULL;
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index 49610d5..0bf05d0 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -43,6 +43,7 @@
 
 // Forward declarations
 class Caches;
+class RenderState;
 class OpenGLRenderer;
 class RenderNode;
 class DeferredDisplayList;
@@ -53,7 +54,7 @@
  */
 class Layer {
 public:
-    Layer(const uint32_t layerWidth, const uint32_t layerHeight);
+    Layer(RenderState& renderState, const uint32_t layerWidth, const uint32_t layerHeight);
     ~Layer();
 
     static uint32_t computeIdealWidth(uint32_t layerWidth);
@@ -85,8 +86,7 @@
         regionRect.translate(layer.left, layer.top);
     }
 
-    void updateDeferred(RenderNode* displayList,
-            int left, int top, int right, int bottom);
+    void updateDeferred(RenderNode* renderNode, int left, int top, int right, int bottom);
 
     inline uint32_t getWidth() const {
         return texture.width;
@@ -297,7 +297,7 @@
      */
     bool deferredUpdateScheduled;
     OpenGLRenderer* renderer;
-    sp<RenderNode> displayList;
+    sp<RenderNode> renderNode;
     Rect dirtyRect;
     bool debugDrawUpdate;
     bool hasDrawnSinceUpdate;
@@ -307,6 +307,8 @@
 
     Caches& caches;
 
+    RenderState& renderState;
+
     /**
      * Name of the FBO used to render the layer. If the name is 0
      * this layer is not backed by an FBO, but a simple texture.
diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp
index 6be0146..13869aa 100644
--- a/libs/hwui/LayerCache.cpp
+++ b/libs/hwui/LayerCache.cpp
@@ -91,7 +91,7 @@
     mCache.clear();
 }
 
-Layer* LayerCache::get(const uint32_t width, const uint32_t height) {
+Layer* LayerCache::get(RenderState& renderState, const uint32_t width, const uint32_t height) {
     Layer* layer = NULL;
 
     LayerEntry entry(width, height);
@@ -108,7 +108,7 @@
     } else {
         LAYER_LOGD("Creating new layer %dx%d", entry.mWidth, entry.mHeight);
 
-        layer = new Layer(entry.mWidth, entry.mHeight);
+        layer = new Layer(renderState, entry.mWidth, entry.mHeight);
         layer->setBlend(true);
         layer->setEmpty(true);
         layer->setFbo(0);
diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h
index 221bfe0..1b0fc2c 100644
--- a/libs/hwui/LayerCache.h
+++ b/libs/hwui/LayerCache.h
@@ -24,6 +24,8 @@
 namespace android {
 namespace uirenderer {
 
+class RenderState;
+
 ///////////////////////////////////////////////////////////////////////////////
 // Defines
 ///////////////////////////////////////////////////////////////////////////////
@@ -55,7 +57,7 @@
      * @param width The desired width of the layer
      * @param height The desired height of the layer
      */
-    Layer* get(const uint32_t width, const uint32_t height);
+    Layer* get(RenderState& renderState, const uint32_t width, const uint32_t height);
 
     /**
      * Adds the layer to the cache. The layer will not be added if there is
diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp
index a5fd375..873baf5 100644
--- a/libs/hwui/LayerRenderer.cpp
+++ b/libs/hwui/LayerRenderer.cpp
@@ -20,6 +20,7 @@
 
 #include <private/hwui/DrawGlInfo.h>
 
+#include "RenderState.h"
 #include "LayerCache.h"
 #include "LayerRenderer.h"
 #include "Matrix.h"
@@ -33,7 +34,9 @@
 // Rendering
 ///////////////////////////////////////////////////////////////////////////////
 
-LayerRenderer::LayerRenderer(Layer* layer): mLayer(layer) {
+LayerRenderer::LayerRenderer(RenderState& renderState, Layer* layer)
+        : OpenGLRenderer(renderState)
+        , mLayer(layer) {
 }
 
 LayerRenderer::~LayerRenderer() {
@@ -43,7 +46,7 @@
         bool opaque) {
     LAYER_RENDERER_LOGD("Rendering into layer, fbo = %d", mLayer->getFbo());
 
-    glBindFramebuffer(GL_FRAMEBUFFER, mLayer->getFbo());
+    renderState().bindFramebuffer(mLayer->getFbo());
 
     const float width = mLayer->layer.getWidth();
     const float height = mLayer->layer.getHeight();
@@ -180,7 +183,7 @@
 // Layers management
 ///////////////////////////////////////////////////////////////////////////////
 
-Layer* LayerRenderer::createRenderLayer(uint32_t width, uint32_t height) {
+Layer* LayerRenderer::createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height) {
     LAYER_RENDERER_LOGD("Requesting new render layer %dx%d", width, height);
 
     Caches& caches = Caches::getInstance();
@@ -191,7 +194,7 @@
     }
 
     caches.activeTexture(0);
-    Layer* layer = caches.layerCache.get(width, height);
+    Layer* layer = caches.layerCache.get(renderState, width, height);
     if (!layer) {
         ALOGW("Could not obtain a layer");
         return NULL;
@@ -221,10 +224,9 @@
     layer->setDirty(true);
     layer->region.clear();
 
-    GLuint previousFbo;
-    glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
+    GLuint previousFbo = renderState.getFramebuffer();
 
-    glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
+    renderState.bindFramebuffer(layer->getFbo());
     layer->bindTexture();
 
     // Initialize the texture if needed
@@ -235,7 +237,7 @@
         // This should only happen if we run out of memory
         if (glGetError() != GL_NO_ERROR) {
             ALOGE("Could not allocate texture for layer (fbo=%d %dx%d)", fbo, width, height);
-            glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+            renderState.bindFramebuffer(previousFbo);
             caches.resourceCache.decrementRefcount(layer);
             return NULL;
         }
@@ -244,7 +246,7 @@
     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
             layer->getTexture(), 0);
 
-    glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+    renderState.bindFramebuffer(previousFbo);
 
     return layer;
 }
@@ -265,10 +267,10 @@
     return true;
 }
 
-Layer* LayerRenderer::createTextureLayer() {
+Layer* LayerRenderer::createTextureLayer(RenderState& renderState) {
     LAYER_RENDERER_LOGD("Creating new texture layer");
 
-    Layer* layer = new Layer(0, 0);
+    Layer* layer = new Layer(renderState, 0, 0);
     layer->setCacheable(false);
     layer->setTextureLayer(true);
     layer->setEmpty(true);
@@ -332,7 +334,7 @@
     }
 }
 
-void LayerRenderer::flushLayer(Layer* layer) {
+void LayerRenderer::flushLayer(RenderState& renderState, Layer* layer) {
 #ifdef GL_EXT_discard_framebuffer
     if (!layer) return;
 
@@ -341,20 +343,23 @@
         // If possible, discard any enqueud operations on deferred
         // rendering architectures
         if (Extensions::getInstance().hasDiscardFramebuffer()) {
-            GLuint previousFbo;
-            glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
-            if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+            GLuint previousFbo = renderState.getFramebuffer();
+            if (fbo != previousFbo) {
+                renderState.bindFramebuffer(fbo);
+            }
 
             const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 };
             glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, attachments);
 
-            if (fbo != previousFbo) glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+            if (fbo != previousFbo) {
+                renderState.bindFramebuffer(previousFbo);
+            }
         }
     }
 #endif
 }
 
-bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) {
+bool LayerRenderer::copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap) {
     Caches& caches = Caches::getInstance();
     if (layer && bitmap->width() <= caches.maxTextureSize &&
             bitmap->height() <= caches.maxTextureSize) {
@@ -369,7 +374,8 @@
 
         GLuint texture;
         GLuint previousFbo;
-        GLuint previousViewport[4];
+        GLsizei previousViewportWidth;
+        GLsizei previousViewportHeight;
 
         GLenum format;
         GLenum type;
@@ -404,9 +410,9 @@
         layer->setAlpha(255, SkXfermode::kSrc_Mode);
         layer->setFbo(fbo);
 
-        glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo);
-        glGetIntegerv(GL_VIEWPORT, (GLint*) &previousViewport);
-        glBindFramebuffer(GL_FRAMEBUFFER, fbo);
+        previousFbo = renderState.getFramebuffer();
+        renderState.getViewport(&previousViewportWidth, &previousViewportHeight);
+        renderState.bindFramebuffer(fbo);
 
         glGenTextures(1, &texture);
         if ((error = glGetError()) != GL_NO_ERROR) goto error;
@@ -431,7 +437,7 @@
         if ((error = glGetError()) != GL_NO_ERROR) goto error;
 
         {
-            LayerRenderer renderer(layer);
+            LayerRenderer renderer(renderState, layer);
             renderer.setViewport(bitmap->width(), bitmap->height());
             renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f,
                     bitmap->width(), bitmap->height(), !layer->isBlend());
@@ -471,13 +477,12 @@
         }
 #endif
 
-        glBindFramebuffer(GL_FRAMEBUFFER, previousFbo);
+        renderState.bindFramebuffer(previousFbo);
         layer->setAlpha(alpha, mode);
         layer->setFbo(previousLayerFbo);
         caches.deleteTexture(texture);
         caches.fboCache.put(fbo);
-        glViewport(previousViewport[0], previousViewport[1],
-                previousViewport[2], previousViewport[3]);
+        renderState.setViewport(previousViewportWidth, previousViewportHeight);
 
         return status;
     }
diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h
index e79e7b8..9202e49 100644
--- a/libs/hwui/LayerRenderer.h
+++ b/libs/hwui/LayerRenderer.h
@@ -27,6 +27,8 @@
 namespace android {
 namespace uirenderer {
 
+class RenderState;
+
 ///////////////////////////////////////////////////////////////////////////////
 // Defines
 ///////////////////////////////////////////////////////////////////////////////
@@ -44,7 +46,7 @@
 
 class LayerRenderer: public OpenGLRenderer {
 public:
-    ANDROID_API LayerRenderer(Layer* layer);
+    LayerRenderer(RenderState& renderState, Layer* layer);
     virtual ~LayerRenderer();
 
     virtual void onViewportInitialized(int width, int height) { /* do nothing */ }
@@ -52,16 +54,16 @@
     virtual status_t clear(float left, float top, float right, float bottom, bool opaque);
     virtual void finish();
 
-    ANDROID_API static Layer* createTextureLayer();
-    ANDROID_API static Layer* createRenderLayer(uint32_t width, uint32_t height);
-    ANDROID_API static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
-    ANDROID_API static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
+    static Layer* createTextureLayer(RenderState& renderState);
+    static Layer* createRenderLayer(RenderState& renderState, uint32_t width, uint32_t height);
+    static bool resizeLayer(Layer* layer, uint32_t width, uint32_t height);
+    static void updateTextureLayer(Layer* layer, uint32_t width, uint32_t height,
             bool isOpaque, bool forceFilter, GLenum renderTarget, float* textureTransform);
-    ANDROID_API static void destroyLayer(Layer* layer);
+    static void destroyLayer(Layer* layer);
     ANDROID_API static void destroyLayerDeferred(Layer* layer);
-    ANDROID_API static bool copyLayer(Layer* layer, SkBitmap* bitmap);
+    static bool copyLayer(RenderState& renderState, Layer* layer, SkBitmap* bitmap);
 
-    static void flushLayer(Layer* layer);
+    static void flushLayer(RenderState& renderState, Layer* layer);
 
 protected:
     virtual void ensureStencilBuffer();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 8f3872a..ede89d7 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -35,6 +35,7 @@
 #include "DeferredDisplayList.h"
 #include "DisplayListRenderer.h"
 #include "Fence.h"
+#include "RenderState.h"
 #include "PathTessellator.h"
 #include "Properties.h"
 #include "ShadowTessellator.h"
@@ -129,8 +130,10 @@
 // Constructors/destructor
 ///////////////////////////////////////////////////////////////////////////////
 
-OpenGLRenderer::OpenGLRenderer():
-        mCaches(Caches::getInstance()), mExtensions(Extensions::getInstance()) {
+OpenGLRenderer::OpenGLRenderer(RenderState& renderState)
+        : mCaches(Caches::getInstance())
+        , mExtensions(Extensions::getInstance())
+        , mRenderState(renderState) {
     // *set* draw modifiers to be 0
     memset(&mDrawModifiers, 0, sizeof(mDrawModifiers));
     mDrawModifiers.mOverrideLayerAlpha = 1.0f;
@@ -187,7 +190,7 @@
 
     discardFramebuffer(mTilingClip.left, mTilingClip.top, mTilingClip.right, mTilingClip.bottom);
 
-    glViewport(0, 0, getWidth(), getHeight());
+    mRenderState.setViewport(getWidth(), getHeight());
 
     // Functors break the tiling extension in pretty spectacular ways
     // This ensures we don't use tiling when a functor is going to be
@@ -311,46 +314,9 @@
     mFrameStarted = false;
 }
 
-void OpenGLRenderer::interrupt() {
-    if (mCaches.currentProgram) {
-        if (mCaches.currentProgram->isInUse()) {
-            mCaches.currentProgram->remove();
-            mCaches.currentProgram = NULL;
-        }
-    }
-    mCaches.resetActiveTexture();
-    mCaches.unbindMeshBuffer();
-    mCaches.unbindIndicesBuffer();
-    mCaches.resetVertexPointers();
-    mCaches.disableTexCoordsVertexArray();
-    debugOverdraw(false, false);
-}
-
-void OpenGLRenderer::resume() {
-    const Snapshot* snapshot = currentSnapshot();
-    glViewport(0, 0, getViewportWidth(), getViewportHeight());
-    glBindFramebuffer(GL_FRAMEBUFFER, snapshot->fbo);
-    debugOverdraw(true, false);
-
-    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-
-    mCaches.scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
-    mCaches.enableScissor();
-    mCaches.resetScissor();
-    dirtyClip();
-
-    mCaches.activeTexture(0);
-    mCaches.resetBoundTextures();
-
-    mCaches.blend = true;
-    glEnable(GL_BLEND);
-    glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
-    glBlendEquation(GL_FUNC_ADD);
-}
-
 void OpenGLRenderer::resumeAfterLayer() {
-    glViewport(0, 0, getViewportWidth(), getViewportHeight());
-    glBindFramebuffer(GL_FRAMEBUFFER, currentSnapshot()->fbo);
+    mRenderState.setViewport(getViewportWidth(), getViewportHeight());
+    mRenderState.bindFramebuffer(currentSnapshot()->fbo);
     debugOverdraw(true, false);
 
     mCaches.resetScissor();
@@ -379,20 +345,19 @@
     info.height = getViewportHeight();
     currentTransform()->copyTo(&info.transform[0]);
 
-    bool dirtyClip = mDirtyClip;
+    bool prevDirtyClip = mDirtyClip;
     // setup GL state for functor
     if (mDirtyClip) {
         setStencilFromClip(); // can issue draws, so must precede enableScissor()/interrupt()
     }
-    if (mCaches.enableScissor() || dirtyClip) {
+    if (mCaches.enableScissor() || prevDirtyClip) {
         setScissorFromClip();
     }
-    interrupt();
 
-    // call functor immediately after GL state setup
-    (*functor)(DrawGlInfo::kModeDraw, &info);
+    mRenderState.invokeFunctor(functor, DrawGlInfo::kModeDraw, &info);
+    // Scissor may have been modified, reset dirty clip
+    dirtyClip();
 
-    resume();
     return DrawGlInfo::kStatusDrew;
 }
 
@@ -413,17 +378,7 @@
 }
 
 void OpenGLRenderer::debugOverdraw(bool enable, bool clear) {
-    if (mCaches.debugOverdraw && getTargetFbo() == 0) {
-        if (clear) {
-            mCaches.disableScissor();
-            mCaches.stencil.clear();
-        }
-        if (enable) {
-            mCaches.stencil.enableDebugWrite();
-        } else {
-            mCaches.stencil.disable();
-        }
-    }
+    mRenderState.debugOverdraw(enable, clear);
 }
 
 void OpenGLRenderer::renderOverdraw() {
@@ -474,8 +429,8 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 bool OpenGLRenderer::updateLayer(Layer* layer, bool inFrame) {
-    if (layer->deferredUpdateScheduled && layer->renderer &&
-            layer->displayList.get() && layer->displayList->isRenderable()) {
+    if (layer->deferredUpdateScheduled && layer->renderer
+            && layer->renderNode.get() && layer->renderNode->isRenderable()) {
         ATRACE_CALL();
 
         Rect& dirty = layer->dirtyRect;
@@ -528,7 +483,7 @@
 
         if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
             mLayerUpdates.clear();
-            glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+            mRenderState.bindFramebuffer(getTargetFbo());
         }
         endMark();
     }
@@ -556,7 +511,7 @@
         }
 
         mLayerUpdates.clear();
-        glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+        mRenderState.bindFramebuffer(getTargetFbo());
 
         endMark();
     }
@@ -620,7 +575,7 @@
     bool restoreLayer = removed.flags & Snapshot::kFlagIsLayer;
 
     if (restoreViewport) {
-        glViewport(0, 0, getViewportWidth(), getViewportHeight());
+        mRenderState.setViewport(getViewportWidth(), getViewportHeight());
     }
 
     if (restoreClip) {
@@ -791,7 +746,7 @@
     }
 
     mCaches.activeTexture(0);
-    Layer* layer = mCaches.layerCache.get(bounds.getWidth(), bounds.getHeight());
+    Layer* layer = mCaches.layerCache.get(mRenderState, bounds.getWidth(), bounds.getHeight());
     if (!layer) {
         return false;
     }
@@ -853,7 +808,7 @@
     endTiling();
     debugOverdraw(false, false);
     // Bind texture to FBO
-    glBindFramebuffer(GL_FRAMEBUFFER, layer->getFbo());
+    mRenderState.bindFramebuffer(layer->getFbo());
     layer->bindTexture();
 
     // Initialize the texture if needed
@@ -876,7 +831,7 @@
     dirtyClip();
 
     // Change the ortho projection
-    glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
+    mRenderState.setViewport(bounds.getWidth(), bounds.getHeight());
     return true;
 }
 
@@ -907,7 +862,7 @@
         layer->removeFbo(false);
 
         // Unbind current FBO and restore previous one
-        glBindFramebuffer(GL_FRAMEBUFFER, restored.fbo);
+        mRenderState.bindFramebuffer(restored.fbo);
         debugOverdraw(true, false);
 
         startTilingCurrentClip();
@@ -1931,25 +1886,24 @@
 // Drawing
 ///////////////////////////////////////////////////////////////////////////////
 
-status_t OpenGLRenderer::drawDisplayList(RenderNode* displayList, Rect& dirty,
-        int32_t replayFlags) {
+status_t OpenGLRenderer::drawRenderNode(RenderNode* renderNode, Rect& dirty, int32_t replayFlags) {
     status_t status;
     // All the usual checks and setup operations (quickReject, setupDraw, etc.)
     // will be performed by the display list itself
-    if (displayList && displayList->isRenderable()) {
+    if (renderNode && renderNode->isRenderable()) {
         // compute 3d ordering
-        displayList->computeOrdering();
+        renderNode->computeOrdering();
         if (CC_UNLIKELY(mCaches.drawDeferDisabled)) {
             status = startFrame();
             ReplayStateStruct replayStruct(*this, dirty, replayFlags);
-            displayList->replayNodeTree(replayStruct);
+            renderNode->replay(replayStruct, 0);
             return status | replayStruct.mDrawGlStatus;
         }
 
         bool avoidOverdraw = !mCaches.debugOverdraw && !mCountOverdraw; // shh, don't tell devs!
         DeferredDisplayList deferredList(*currentClipRect(), avoidOverdraw);
         DeferStateStruct deferStruct(deferredList, *this, replayFlags);
-        displayList->deferNodeTree(deferStruct);
+        renderNode->defer(deferStruct, 0);
 
         flushLayers();
         status = startFrame();
@@ -2556,8 +2510,8 @@
         return drawShape(left, top, texture, p);
     }
 
-    const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(*currentTransform(),
-            right - left, bottom - top, rx, ry, p);
+    const VertexBuffer* vertexBuffer = mCaches.tessellationCache.getRoundRect(
+            *currentTransform(), *p, right - left, bottom - top, rx, ry);
     return drawVertexBuffer(left, top, *vertexBuffer, p);
 }
 
@@ -2611,10 +2565,6 @@
         return DrawGlInfo::kStatusDone;
     }
 
-    if (fabs(sweepAngle) >= 360.0f) {
-        return drawOval(left, top, right, bottom, p);
-    }
-
     // TODO: support fills (accounting for concavity if useCenter && sweepAngle > 180)
     if (p->getStyle() != SkPaint::kStroke_Style || p->getPathEffect() != 0 || useCenter) {
         mCaches.activeTexture(0);
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 346a65c..4ff5780 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -57,6 +57,7 @@
 namespace uirenderer {
 
 class DeferredDisplayState;
+class RenderState;
 class RenderNode;
 class TextSetupFunctor;
 class VertexBuffer;
@@ -119,33 +120,31 @@
  */
 class OpenGLRenderer : public StatefulBaseRenderer {
 public:
-    ANDROID_API OpenGLRenderer();
+    OpenGLRenderer(RenderState& renderState);
     virtual ~OpenGLRenderer();
 
-    ANDROID_API void initProperties();
+    void initProperties();
 
     virtual void onViewportInitialized();
     virtual status_t prepareDirty(float left, float top, float right, float bottom, bool opaque);
     virtual void finish();
-    virtual void interrupt();
-    virtual void resume();
 
-    ANDROID_API void setCountOverdrawEnabled(bool enabled) {
+    void setCountOverdrawEnabled(bool enabled) {
         mCountOverdraw = enabled;
     }
 
-    ANDROID_API float getOverdraw() {
+    float getOverdraw() {
         return mCountOverdraw ? mOverdraw : 0.0f;
     }
 
     virtual status_t callDrawGLFunction(Functor* functor, Rect& dirty);
 
-    ANDROID_API void pushLayerUpdate(Layer* layer);
-    ANDROID_API void cancelLayerUpdate(Layer* layer);
-    ANDROID_API void clearLayerUpdates();
-    ANDROID_API void flushLayerUpdates();
+    void pushLayerUpdate(Layer* layer);
+    void cancelLayerUpdate(Layer* layer);
+    void clearLayerUpdates();
+    void flushLayerUpdates();
 
-    ANDROID_API virtual int saveLayer(float left, float top, float right, float bottom,
+    virtual int saveLayer(float left, float top, float right, float bottom,
             const SkPaint* paint, int flags) {
         return saveLayer(left, top, right, bottom, paint, flags, NULL);
     }
@@ -158,7 +157,7 @@
     int saveLayerDeferred(float left, float top, float right, float bottom,
             const SkPaint* paint, int flags);
 
-    virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1);
+    virtual status_t drawRenderNode(RenderNode* displayList, Rect& dirty, int32_t replayFlags = 1);
     virtual status_t drawLayer(Layer* layer, float x, float y);
     virtual status_t drawBitmap(const SkBitmap* bitmap, float left, float top,
             const SkPaint* paint);
@@ -231,7 +230,7 @@
     const DrawModifiers& getDrawModifiers() { return mDrawModifiers; }
     void setDrawModifiers(const DrawModifiers& drawModifiers) { mDrawModifiers = drawModifiers; }
 
-    ANDROID_API bool isCurrentTransformSimple() {
+    bool isCurrentTransformSimple() {
         return currentTransform()->isSimple();
     }
 
@@ -474,6 +473,8 @@
         return false;
     }
 
+    inline RenderState& renderState() { return mRenderState; }
+
 private:
     /**
      * Discards the content of the framebuffer if supported by the driver.
@@ -977,6 +978,7 @@
     // Various caches
     Caches& mCaches;
     Extensions& mExtensions;
+    RenderState& mRenderState;
 
     // List of rectangles to clear after saveLayer() is invoked
     Vector<Rect*> mLayers;
@@ -1012,7 +1014,6 @@
 
     bool mSkipOutlineClip;
 
-    friend class DisplayListRenderer;
     friend class Layer;
     friend class TextSetupFunctor;
     friend class DrawBitmapOp;
diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp
index 9dd5aa5..97eb583 100644
--- a/libs/hwui/PathCache.cpp
+++ b/libs/hwui/PathCache.cpp
@@ -104,8 +104,7 @@
 }
 
 static void initBitmap(SkBitmap& bitmap, uint32_t width, uint32_t height) {
-    bitmap.setConfig(SkBitmap::kA8_Config, width, height);
-    bitmap.allocPixels();
+    bitmap.allocPixels(SkImageInfo::MakeA8(width, height));
     bitmap.eraseColor(0);
 }
 
diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp
index fc51170..131384a 100644
--- a/libs/hwui/RenderNode.cpp
+++ b/libs/hwui/RenderNode.cpp
@@ -15,11 +15,12 @@
  */
 
 #define ATRACE_TAG ATRACE_TAG_VIEW
-#define LOG_TAG "RenderNode"
+#define LOG_TAG "OpenGLRenderer"
 
 #include "RenderNode.h"
 
 #include <algorithm>
+#include <string>
 
 #include <SkCanvas.h>
 #include <algorithm>
@@ -117,7 +118,7 @@
 }
 
 void RenderNode::damageSelf(TreeInfo& info) {
-    if (isRenderable() && properties().getAlpha() > 0) {
+    if (isRenderable()) {
         if (properties().getClipDamageToBounds()) {
             info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
         } else {
@@ -154,11 +155,14 @@
     }
 
     if (!mLayer) {
-        mLayer = LayerRenderer::createRenderLayer(getWidth(), getHeight());
+        mLayer = LayerRenderer::createRenderLayer(info.renderState, getWidth(), getHeight());
         applyLayerPropertiesToLayer(info);
         damageSelf(info);
     } else if (mLayer->layer.getWidth() != getWidth() || mLayer->layer.getHeight() != getHeight()) {
-        LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight());
+        if (!LayerRenderer::resizeLayer(mLayer, getWidth(), getHeight())) {
+            LayerRenderer::destroyLayer(mLayer);
+            mLayer = 0;
+        }
         damageSelf(info);
     }
 
@@ -166,6 +170,15 @@
     info.damageAccumulator->peekAtDirty(&dirty);
     info.damageAccumulator->popTransform();
 
+    if (!mLayer) {
+        if (info.errorHandler) {
+            std::string msg = "Unable to create layer for ";
+            msg += getName();
+            info.errorHandler->onError(msg);
+        }
+        return;
+    }
+
     if (!dirty.isEmpty()) {
         mLayer->updateDeferred(this, dirty.fLeft, dirty.fTop, dirty.fRight, dirty.fBottom);
     }
@@ -254,7 +267,7 @@
         mNeedsDisplayListDataSync = false;
         // Do a push pass on the old tree to handle freeing DisplayListData
         // that are no longer used
-        TreeInfo oldTreeInfo(TreeInfo::MODE_MAYBE_DETACHING);
+        TreeInfo oldTreeInfo(TreeInfo::MODE_MAYBE_DETACHING, info.renderState);
         oldTreeInfo.damageAccumulator = info.damageAccumulator;
         prepareSubTree(oldTreeInfo, mDisplayListData);
         delete mDisplayListData;
@@ -310,8 +323,8 @@
             info.prepareTextures = cache.prefetchAndMarkInUse(subtree->bitmapResources[i]);
         }
         for (size_t i = 0; i < subtree->children().size(); i++) {
-            DrawDisplayListOp* op = subtree->children()[i];
-            RenderNode* childNode = op->mDisplayList;
+            DrawRenderNodeOp* op = subtree->children()[i];
+            RenderNode* childNode = op->mRenderNode;
             info.damageAccumulator->pushTransform(&op->mTransformFromParent);
             childNode->prepareTreeImpl(info);
             info.damageAccumulator->popTransform();
@@ -442,16 +455,16 @@
     // transform properties are applied correctly to top level children
     if (mDisplayListData == NULL) return;
     for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
-        DrawDisplayListOp* childOp = mDisplayListData->children()[i];
-        childOp->mDisplayList->computeOrderingImpl(childOp,
+        DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
+        childOp->mRenderNode->computeOrderingImpl(childOp,
                 properties().getOutline().getPath(), &mProjectedNodes, &mat4::identity());
     }
 }
 
 void RenderNode::computeOrderingImpl(
-        DrawDisplayListOp* opState,
+        DrawRenderNodeOp* opState,
         const SkPath* outlineOfProjectionSurface,
-        Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+        Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
         const mat4* transformFromProjectionSurface) {
     mProjectedNodes.clear();
     if (mDisplayListData == NULL || mDisplayListData->isEmpty()) return;
@@ -475,11 +488,11 @@
         const bool isProjectionReceiver = mDisplayListData->projectionReceiveIndex >= 0;
         bool haveAppliedPropertiesToProjection = false;
         for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
-            DrawDisplayListOp* childOp = mDisplayListData->children()[i];
-            RenderNode* child = childOp->mDisplayList;
+            DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
+            RenderNode* child = childOp->mRenderNode;
 
             const SkPath* projectionOutline = NULL;
-            Vector<DrawDisplayListOp*>* projectionChildren = NULL;
+            Vector<DrawRenderNodeOp*>* projectionChildren = NULL;
             const mat4* projectionTransform = NULL;
             if (isProjectionReceiver && !child->properties().getProjectBackwards()) {
                 // if receiving projections, collect projecting descendent
@@ -523,15 +536,7 @@
     const int mLevel;
 };
 
-void RenderNode::deferNodeTree(DeferStateStruct& deferStruct) {
-    DeferOperationHandler handler(deferStruct, 0);
-    if (MathUtils::isPositive(properties().getZ())) {
-        issueDrawShadowOperation(Matrix4::identity(), handler);
-    }
-    issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
-}
-
-void RenderNode::deferNodeInParent(DeferStateStruct& deferStruct, const int level) {
+void RenderNode::defer(DeferStateStruct& deferStruct, const int level) {
     DeferOperationHandler handler(deferStruct, level);
     issueOperations<DeferOperationHandler>(deferStruct.mRenderer, handler);
 }
@@ -561,29 +566,21 @@
     const int mLevel;
 };
 
-void RenderNode::replayNodeTree(ReplayStateStruct& replayStruct) {
-    ReplayOperationHandler handler(replayStruct, 0);
-    if (MathUtils::isPositive(properties().getZ())) {
-        issueDrawShadowOperation(Matrix4::identity(), handler);
-    }
-    issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
-}
-
-void RenderNode::replayNodeInParent(ReplayStateStruct& replayStruct, const int level) {
+void RenderNode::replay(ReplayStateStruct& replayStruct, const int level) {
     ReplayOperationHandler handler(replayStruct, level);
     issueOperations<ReplayOperationHandler>(replayStruct.mRenderer, handler);
 }
 
-void RenderNode::buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes) {
+void RenderNode::buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes) {
     if (mDisplayListData == NULL || mDisplayListData->children().size() == 0) return;
 
     for (unsigned int i = 0; i < mDisplayListData->children().size(); i++) {
-        DrawDisplayListOp* childOp = mDisplayListData->children()[i];
-        RenderNode* child = childOp->mDisplayList;
+        DrawRenderNodeOp* childOp = mDisplayListData->children()[i];
+        RenderNode* child = childOp->mRenderNode;
         float childZ = child->properties().getZ();
 
         if (!MathUtils::isZero(childZ)) {
-            zTranslatedNodes.add(ZDrawDisplayListOpPair(childZ, childOp));
+            zTranslatedNodes.add(ZDrawRenderNodeOpPair(childZ, childOp));
             childOp->mSkipInOrderDraw = true;
         } else if (!child->properties().getProjectBackwards()) {
             // regular, in order drawing DisplayList
@@ -628,10 +625,40 @@
     handler(shadowOp, PROPERTY_SAVECOUNT, properties().getClipToBounds());
 }
 
+template <class T>
+int RenderNode::issueOperationsOfNegZChildren(
+        const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+        OpenGLRenderer& renderer, T& handler) {
+    if (zTranslatedNodes.isEmpty()) return -1;
+
+    // create a save around the body of the ViewGroup's draw method, so that
+    // matrix/clip methods don't affect composited children
+    int shadowSaveCount = renderer.getSaveCount();
+    handler(new (handler.allocator()) SaveOp(SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag),
+            PROPERTY_SAVECOUNT, properties().getClipToBounds());
+
+    issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
+    return shadowSaveCount;
+}
+
+template <class T>
+void RenderNode::issueOperationsOfPosZChildren(int shadowRestoreTo,
+        const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+        OpenGLRenderer& renderer, T& handler) {
+    if (zTranslatedNodes.isEmpty()) return;
+
+    LOG_ALWAYS_FATAL_IF(shadowRestoreTo < 0, "invalid save to restore to");
+    handler(new (handler.allocator()) RestoreToCountOp(shadowRestoreTo),
+            PROPERTY_SAVECOUNT, properties().getClipToBounds());
+    renderer.setOverrideLayerAlpha(1.0f);
+
+    issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+}
+
 #define SHADOW_DELTA 0.1f
 
 template <class T>
-void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+void RenderNode::issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
         ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler) {
     const int size = zTranslatedNodes.size();
     if (size == 0
@@ -666,8 +693,8 @@
     float lastCasterZ = 0.0f;
     while (shadowIndex < endIndex || drawIndex < endIndex) {
         if (shadowIndex < endIndex) {
-            DrawDisplayListOp* casterOp = zTranslatedNodes[shadowIndex].value;
-            RenderNode* caster = casterOp->mDisplayList;
+            DrawRenderNodeOp* casterOp = zTranslatedNodes[shadowIndex].value;
+            RenderNode* caster = casterOp->mRenderNode;
             const float casterZ = zTranslatedNodes[shadowIndex].key;
             // attempt to render the shadow if the caster about to be drawn is its caster,
             // OR if its caster's Z value is similar to the previous potential caster
@@ -684,8 +711,8 @@
         // since it modifies the renderer's matrix
         int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
 
-        DrawDisplayListOp* childOp = zTranslatedNodes[drawIndex].value;
-        RenderNode* child = childOp->mDisplayList;
+        DrawRenderNodeOp* childOp = zTranslatedNodes[drawIndex].value;
+        RenderNode* child = childOp->mRenderNode;
 
         renderer.concatMatrix(childOp->mTransformFromParent);
         childOp->mSkipInOrderDraw = false; // this is horrible, I'm so sorry everyone
@@ -701,8 +728,6 @@
 void RenderNode::issueOperationsOfProjectedChildren(OpenGLRenderer& renderer, T& handler) {
     DISPLAY_LIST_LOGD("%*s%d projected children:", (handler.level() + 1) * 2, "", mProjectedNodes.size());
     const SkPath* projectionReceiverOutline = properties().getOutline().getPath();
-    bool maskProjecteesWithPath = projectionReceiverOutline != NULL
-            && !projectionReceiverOutline->isRect(NULL);
     int restoreTo = renderer.getSaveCount();
 
     // If the projection reciever has an outline, we mask each of the projected rendernodes to it
@@ -723,7 +748,7 @@
             SaveLayerOp* op = new (alloc) SaveLayerOp(
                     outlineBounds.left(), outlineBounds.top(),
                     outlineBounds.right(), outlineBounds.bottom(),
-                    255, SkCanvas::kARGB_ClipLayer_SaveFlag);
+                    255, SkCanvas::kMatrix_SaveFlag | SkCanvas::kClip_SaveFlag | SkCanvas::kARGB_ClipLayer_SaveFlag);
             op->setMask(projectionReceiverOutline);
             handler(op, PROPERTY_SAVECOUNT, properties().getClipToBounds());
 
@@ -736,7 +761,7 @@
 
     // draw projected nodes
     for (size_t i = 0; i < mProjectedNodes.size(); i++) {
-        DrawDisplayListOp* childOp = mProjectedNodes[i];
+        DrawRenderNodeOp* childOp = mProjectedNodes[i];
 
         // matrix save, concat, and restore can be done safely without allocating operations
         int restoreTo = renderer.save(SkCanvas::kMatrix_SaveFlag);
@@ -808,11 +833,11 @@
             handler(new (alloc) DrawLayerOp(mLayer, 0, 0),
                     renderer.getSaveCount() - 1, properties().getClipToBounds());
         } else {
-            Vector<ZDrawDisplayListOpPair> zTranslatedNodes;
+            Vector<ZDrawRenderNodeOpPair> zTranslatedNodes;
             buildZSortedChildList(zTranslatedNodes);
 
             // for 3d root, draw children with negative z values
-            issueOperationsOf3dChildren(zTranslatedNodes, kNegativeZChildren, renderer, handler);
+            int shadowRestoreTo = issueOperationsOfNegZChildren(zTranslatedNodes, renderer, handler);
 
             DisplayListLogBuffer& logBuffer = DisplayListLogBuffer::getInstance();
             const int saveCountOffset = renderer.getSaveCount() - 1;
@@ -820,9 +845,9 @@
             for (unsigned int i = 0; i < mDisplayListData->displayListOps.size(); i++) {
                 DisplayListOp *op = mDisplayListData->displayListOps[i];
 
-    #if DEBUG_DISPLAY_LIST
+#if DEBUG_DISPLAY_LIST
                 op->output(level + 1);
-    #endif
+#endif
                 logBuffer.writeCommand(level, op->name());
                 handler(op, saveCountOffset, properties().getClipToBounds());
 
@@ -832,7 +857,7 @@
             }
 
             // for 3d root, draw children with positive z values
-            issueOperationsOf3dChildren(zTranslatedNodes, kPositiveZChildren, renderer, handler);
+            issueOperationsOfPosZChildren(shadowRestoreTo, zTranslatedNodes, renderer, handler);
         }
     }
 
diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h
index b2fe849..3980dad 100644
--- a/libs/hwui/RenderNode.h
+++ b/libs/hwui/RenderNode.h
@@ -55,7 +55,6 @@
 namespace android {
 namespace uirenderer {
 
-class DeferredDisplayList;
 class DisplayListOp;
 class DisplayListRenderer;
 class OpenGLRenderer;
@@ -67,7 +66,7 @@
 class SaveLayerOp;
 class SaveOp;
 class RestoreToCountOp;
-class DrawDisplayListOp;
+class DrawRenderNodeOp;
 
 /**
  * Primary class for storing recorded canvas commands, as well as per-View/ViewGroup display properties.
@@ -113,11 +112,8 @@
 
     void computeOrdering();
 
-    void deferNodeTree(DeferStateStruct& deferStruct);
-    void deferNodeInParent(DeferStateStruct& deferStruct, const int level);
-
-    void replayNodeTree(ReplayStateStruct& replayStruct);
-    void replayNodeInParent(ReplayStateStruct& replayStruct, const int level);
+    void defer(DeferStateStruct& deferStruct, const int level);
+    void replay(ReplayStateStruct& replayStruct, const int level);
 
     ANDROID_API void output(uint32_t level = 1);
     ANDROID_API int getDebugSize();
@@ -189,6 +185,8 @@
     // UI thread only!
     ANDROID_API void removeAnimator(const sp<BaseRenderNodeAnimator>& animator) {
         mStagingAnimators.erase(animator);
+        // Force a sync of the staging property value
+        mDirtyPropertyFields |= animator->dirtyMask();
         mNeedsAnimatorsSync = true;
     }
 
@@ -196,9 +194,9 @@
     virtual void damageSelf(TreeInfo& info);
 
 private:
-    typedef key_value_pair_t<float, DrawDisplayListOp*> ZDrawDisplayListOpPair;
+    typedef key_value_pair_t<float, DrawRenderNodeOp*> ZDrawRenderNodeOpPair;
 
-    static size_t findNonNegativeIndex(const Vector<ZDrawDisplayListOpPair>& nodes) {
+    static size_t findNonNegativeIndex(const Vector<ZDrawRenderNodeOpPair>& nodes) {
         for (size_t i = 0; i < nodes.size(); i++) {
             if (nodes[i].key >= 0.0f) return i;
         }
@@ -212,21 +210,29 @@
 
     void applyViewPropertyTransforms(mat4& matrix, bool true3dTransform = false);
 
-    void computeOrderingImpl(DrawDisplayListOp* opState,
+    void computeOrderingImpl(DrawRenderNodeOp* opState,
             const SkPath* outlineOfProjectionSurface,
-            Vector<DrawDisplayListOp*>* compositedChildrenOfProjectionSurface,
+            Vector<DrawRenderNodeOp*>* compositedChildrenOfProjectionSurface,
             const mat4* transformFromProjectionSurface);
 
     template <class T>
     inline void setViewProperties(OpenGLRenderer& renderer, T& handler);
 
-    void buildZSortedChildList(Vector<ZDrawDisplayListOpPair>& zTranslatedNodes);
+    void buildZSortedChildList(Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes);
 
     template<class T>
     inline void issueDrawShadowOperation(const Matrix4& transformFromParent, T& handler);
 
     template <class T>
-    inline void issueOperationsOf3dChildren(const Vector<ZDrawDisplayListOpPair>& zTranslatedNodes,
+    inline int issueOperationsOfNegZChildren(
+            const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+            OpenGLRenderer& renderer, T& handler);
+    template <class T>
+    inline void issueOperationsOfPosZChildren(int shadowRestoreTo,
+            const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
+            OpenGLRenderer& renderer, T& handler);
+    template <class T>
+    inline void issueOperationsOf3dChildren(const Vector<ZDrawRenderNodeOpPair>& zTranslatedNodes,
             ChildrenSelectMode mode, OpenGLRenderer& renderer, T& handler);
 
     template <class T>
@@ -234,7 +240,7 @@
 
     /**
      * Issue the RenderNode's operations into a handler, recursing for subtrees through
-     * DrawDisplayListOp's defer() or replay() methods
+     * DrawRenderNodeOp's defer() or replay() methods
      */
     template <class T>
     inline void issueOperations(OpenGLRenderer& renderer, T& handler);
@@ -285,7 +291,7 @@
      */
 
     // for projection surfaces, contains a list of all children items
-    Vector<DrawDisplayListOp*> mProjectedNodes;
+    Vector<DrawRenderNodeOp*> mProjectedNodes;
 }; // class RenderNode
 
 } /* namespace uirenderer */
diff --git a/libs/hwui/RenderState.cpp b/libs/hwui/RenderState.cpp
new file mode 100644
index 0000000..97f379d
--- /dev/null
+++ b/libs/hwui/RenderState.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#include "RenderState.h"
+
+namespace android {
+namespace uirenderer {
+
+RenderState::RenderState()
+        : mCaches(NULL)
+        , mViewportWidth(0)
+        , mViewportHeight(0)
+        , mFramebuffer(0) {
+}
+
+RenderState::~RenderState() {
+}
+
+void RenderState::onGLContextCreated() {
+    // This is delayed because the first access of Caches makes GL calls
+    mCaches = &Caches::getInstance();
+    mCaches->init();
+}
+
+void RenderState::setViewport(GLsizei width, GLsizei height) {
+    mViewportWidth = width;
+    mViewportHeight = height;
+    glViewport(0, 0, mViewportWidth, mViewportHeight);
+}
+
+
+void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) {
+    *outWidth = mViewportWidth;
+    *outHeight = mViewportHeight;
+}
+
+void RenderState::bindFramebuffer(GLuint fbo) {
+    if (mFramebuffer != fbo) {
+        mFramebuffer = fbo;
+        glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+    }
+}
+
+void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) {
+    interruptForFunctorInvoke();
+    (*functor)(mode, info);
+    resumeFromFunctorInvoke();
+}
+
+void RenderState::interruptForFunctorInvoke() {
+    if (mCaches->currentProgram) {
+        if (mCaches->currentProgram->isInUse()) {
+            mCaches->currentProgram->remove();
+            mCaches->currentProgram = NULL;
+        }
+    }
+    mCaches->resetActiveTexture();
+    mCaches->unbindMeshBuffer();
+    mCaches->unbindIndicesBuffer();
+    mCaches->resetVertexPointers();
+    mCaches->disableTexCoordsVertexArray();
+    debugOverdraw(false, false);
+}
+
+void RenderState::resumeFromFunctorInvoke() {
+    glViewport(0, 0, mViewportWidth, mViewportHeight);
+    glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer);
+    debugOverdraw(false, false);
+
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+
+    mCaches->scissorEnabled = glIsEnabled(GL_SCISSOR_TEST);
+    mCaches->enableScissor();
+    mCaches->resetScissor();
+
+    mCaches->activeTexture(0);
+    mCaches->resetBoundTextures();
+
+    mCaches->blend = true;
+    glEnable(GL_BLEND);
+    glBlendFunc(mCaches->lastSrcMode, mCaches->lastDstMode);
+    glBlendEquation(GL_FUNC_ADD);
+}
+
+void RenderState::debugOverdraw(bool enable, bool clear) {
+    if (mCaches->debugOverdraw && mFramebuffer == 0) {
+        if (clear) {
+            mCaches->disableScissor();
+            mCaches->stencil.clear();
+        }
+        if (enable) {
+            mCaches->stencil.enableDebugWrite();
+        } else {
+            mCaches->stencil.disable();
+        }
+    }
+}
+
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/RenderState.h b/libs/hwui/RenderState.h
new file mode 100644
index 0000000..f7116e2
--- /dev/null
+++ b/libs/hwui/RenderState.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef RENDERSTATE_H
+#define RENDERSTATE_H
+
+#include <GLES2/gl2.h>
+#include <GLES2/gl2ext.h>
+
+#include <private/hwui/DrawGlInfo.h>
+
+#include "Caches.h"
+#include "utils/Macros.h"
+
+namespace android {
+namespace uirenderer {
+
+namespace renderthread {
+class RenderThread;
+}
+
+// TODO: Replace Cache's GL state tracking with this. For now it's more a thin
+// wrapper of Caches for users to migrate to.
+class RenderState {
+    PREVENT_COPY_AND_ASSIGN(RenderState);
+public:
+    void onGLContextCreated();
+
+    void setViewport(GLsizei width, GLsizei height);
+    void getViewport(GLsizei* outWidth, GLsizei* outHeight);
+
+    void bindFramebuffer(GLuint fbo);
+    GLint getFramebuffer() { return mFramebuffer; }
+
+    void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info);
+
+    void debugOverdraw(bool enable, bool clear);
+
+private:
+    friend class renderthread::RenderThread;
+
+    void interruptForFunctorInvoke();
+    void resumeFromFunctorInvoke();
+
+    RenderState();
+    ~RenderState();
+
+    Caches* mCaches;
+
+    GLsizei mViewportWidth;
+    GLsizei mViewportHeight;
+    GLuint mFramebuffer;
+};
+
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* RENDERSTATE_H */
diff --git a/libs/hwui/Renderer.h b/libs/hwui/Renderer.h
index 320895c..2ec99c9 100644
--- a/libs/hwui/Renderer.h
+++ b/libs/hwui/Renderer.h
@@ -129,21 +129,6 @@
      */
     virtual void finish() = 0;
 
-    /**
-     * This method must be invoked before handing control over to a draw functor.
-     * See callDrawGLFunction() for instance.
-     *
-     * This command must not be recorded inside display lists.
-     */
-    virtual void interrupt() = 0;
-
-    /**
-     * This method must be invoked after getting control back from a draw functor.
-     *
-     * This command must not be recorded inside display lists.
-     */
-    virtual void resume() = 0;
-
 // ----------------------------------------------------------------------------
 // Canvas state operations
 // ----------------------------------------------------------------------------
@@ -233,7 +218,7 @@
 // Canvas draw operations - special
 // ----------------------------------------------------------------------------
     virtual status_t drawLayer(Layer* layer, float x, float y) = 0;
-    virtual status_t drawDisplayList(RenderNode* displayList, Rect& dirty,
+    virtual status_t drawRenderNode(RenderNode* renderNode, Rect& dirty,
             int32_t replayFlags) = 0;
 
     // TODO: rename for consistency
diff --git a/libs/hwui/TessellationCache.cpp b/libs/hwui/TessellationCache.cpp
index ef3d0d7..08b54ff 100644
--- a/libs/hwui/TessellationCache.cpp
+++ b/libs/hwui/TessellationCache.cpp
@@ -39,6 +39,8 @@
 
 TessellationCache::Description::Description()
         : type(kNone)
+        , scaleX(1.0f)
+        , scaleY(1.0f)
         , aa(false)
         , cap(SkPaint::kDefault_Cap)
         , style(SkPaint::kFill_Style)
@@ -46,21 +48,13 @@
     memset(&shape, 0, sizeof(Shape));
 }
 
-TessellationCache::Description::Description(Type type)
+TessellationCache::Description::Description(Type type, const Matrix4& transform, const SkPaint& paint)
         : type(type)
-        , aa(false)
-        , cap(SkPaint::kDefault_Cap)
-        , style(SkPaint::kFill_Style)
-        , strokeWidth(1.0f) {
-    memset(&shape, 0, sizeof(Shape));
-}
-
-TessellationCache::Description::Description(Type type, const SkPaint* paint)
-        : type(type)
-        , aa(paint->isAntiAlias())
-        , cap(paint->getStrokeCap())
-        , style(paint->getStyle())
-        , strokeWidth(paint->getStrokeWidth()) {
+        , aa(paint.isAntiAlias())
+        , cap(paint.getStrokeCap())
+        , style(paint.getStyle())
+        , strokeWidth(paint.getStrokeWidth()) {
+    PathTessellator::extractTessellationScales(transform, &scaleX, &scaleY);
     memset(&shape, 0, sizeof(Shape));
 }
 
@@ -70,10 +64,20 @@
     hash = JenkinsHashMix(hash, cap);
     hash = JenkinsHashMix(hash, style);
     hash = JenkinsHashMix(hash, android::hash_type(strokeWidth));
+    hash = JenkinsHashMix(hash, android::hash_type(scaleX));
+    hash = JenkinsHashMix(hash, android::hash_type(scaleY));
     hash = JenkinsHashMixBytes(hash, (uint8_t*) &shape, sizeof(Shape));
     return JenkinsHashWhiten(hash);
 }
 
+void TessellationCache::Description::setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const {
+    matrix->loadScale(scaleX, scaleY, 1.0f);
+    paint->setAntiAlias(aa);
+    paint->setStrokeCap(cap);
+    paint->setStyle(style);
+    paint->setStrokeWidth(strokeWidth);
+}
+
 TessellationCache::ShadowDescription::ShadowDescription()
         : nodeKey(NULL) {
     memset(&matrixData, 0, 16 * sizeof(float));
@@ -96,20 +100,15 @@
 
 class TessellationCache::TessellationTask : public Task<VertexBuffer*> {
 public:
-    TessellationTask(Tessellator tessellator, const Description& description,
-                const SkPaint* paint)
+    TessellationTask(Tessellator tessellator, const Description& description)
         : tessellator(tessellator)
-        , description(description)
-        , paint(*paint) {
+        , description(description) {
     }
 
     ~TessellationTask() {}
 
     Tessellator tessellator;
     Description description;
-
-    //copied, since input paint may not be immutable
-    const SkPaint paint;
 };
 
 class TessellationCache::TessellationProcessor : public TaskProcessor<VertexBuffer*> {
@@ -121,7 +120,7 @@
     virtual void onProcess(const sp<Task<VertexBuffer*> >& task) {
         TessellationTask* t = static_cast<TessellationTask*>(task.get());
         ATRACE_NAME("shape tessellation");
-        VertexBuffer* buffer = t->tessellator(t->description, t->paint);
+        VertexBuffer* buffer = t->tessellator(t->description);
         t->setResult(buffer);
     }
 };
@@ -416,21 +415,12 @@
 // Tessellation precaching
 ///////////////////////////////////////////////////////////////////////////////
 
-static VertexBuffer* tessellatePath(const SkPath& path, const SkPaint* paint,
-        float scaleX, float scaleY) {
-    VertexBuffer* buffer = new VertexBuffer();
-    Matrix4 matrix;
-    matrix.loadScale(scaleX, scaleY, 1);
-    PathTessellator::tessellatePath(path, paint, matrix, *buffer);
-    return buffer;
-}
-
 TessellationCache::Buffer* TessellationCache::getOrCreateBuffer(
-        const Description& entry, Tessellator tessellator, const SkPaint* paint) {
+        const Description& entry, Tessellator tessellator) {
     Buffer* buffer = mCache.get(entry);
     if (!buffer) {
         // not cached, enqueue a task to fill the buffer
-        sp<TessellationTask> task = new TessellationTask(tessellator, entry, paint);
+        sp<TessellationTask> task = new TessellationTask(tessellator, entry);
         buffer = new Buffer(task);
 
         if (mProcessor == NULL) {
@@ -442,43 +432,49 @@
     return buffer;
 }
 
+static VertexBuffer* tessellatePath(const TessellationCache::Description& description,
+        const SkPath& path) {
+    Matrix4 matrix;
+    SkPaint paint;
+    description.setupMatrixAndPaint(&matrix, &paint);
+    VertexBuffer* buffer = new VertexBuffer();
+    PathTessellator::tessellatePath(path, &paint, matrix, *buffer);
+    return buffer;
+}
+
 ///////////////////////////////////////////////////////////////////////////////
-// Rounded rects
+// RoundRect
 ///////////////////////////////////////////////////////////////////////////////
 
-static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description,
-        const SkPaint& paint) {
-    SkRect rect = SkRect::MakeWH(description.shape.roundRect.mWidth,
-            description.shape.roundRect.mHeight);
-    float rx = description.shape.roundRect.mRx;
-    float ry = description.shape.roundRect.mRy;
-    if (paint.getStyle() == SkPaint::kStrokeAndFill_Style) {
-        float outset = paint.getStrokeWidth() / 2;
+static VertexBuffer* tessellateRoundRect(const TessellationCache::Description& description) {
+    SkRect rect = SkRect::MakeWH(description.shape.roundRect.width,
+            description.shape.roundRect.height);
+    float rx = description.shape.roundRect.rx;
+    float ry = description.shape.roundRect.ry;
+    if (description.style == SkPaint::kStrokeAndFill_Style) {
+        float outset = description.strokeWidth / 2;
         rect.outset(outset, outset);
         rx += outset;
         ry += outset;
     }
     SkPath path;
     path.addRoundRect(rect, rx, ry);
-    return tessellatePath(path, &paint,
-            description.shape.roundRect.mScaleX, description.shape.roundRect.mScaleY);
+    return tessellatePath(description, path);
 }
 
-TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(const Matrix4& transform,
-        float width, float height, float rx, float ry, const SkPaint* paint) {
-    Description entry(Description::kRoundRect, paint);
-    entry.shape.roundRect.mWidth = width;
-    entry.shape.roundRect.mHeight = height;
-    entry.shape.roundRect.mRx = rx;
-    entry.shape.roundRect.mRy = ry;
-    PathTessellator::extractTessellationScales(transform,
-            &entry.shape.roundRect.mScaleX, &entry.shape.roundRect.mScaleY);
-
-    return getOrCreateBuffer(entry, &tessellateRoundRect, paint);
+TessellationCache::Buffer* TessellationCache::getRoundRectBuffer(
+        const Matrix4& transform, const SkPaint& paint,
+        float width, float height, float rx, float ry) {
+    Description entry(Description::kRoundRect, transform, paint);
+    entry.shape.roundRect.width = width;
+    entry.shape.roundRect.height = height;
+    entry.shape.roundRect.rx = rx;
+    entry.shape.roundRect.ry = ry;
+    return getOrCreateBuffer(entry, &tessellateRoundRect);
 }
-const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform,
-        float width, float height, float rx, float ry, const SkPaint* paint) {
-    return getRoundRectBuffer(transform, width, height, rx, ry, paint)->getVertexBuffer();
+const VertexBuffer* TessellationCache::getRoundRect(const Matrix4& transform, const SkPaint& paint,
+        float width, float height, float rx, float ry) {
+    return getRoundRectBuffer(transform, paint, width, height, rx, ry)->getVertexBuffer();
 }
 
 }; // namespace uirenderer
diff --git a/libs/hwui/TessellationCache.h b/libs/hwui/TessellationCache.h
index d4ff943..688a699 100644
--- a/libs/hwui/TessellationCache.h
+++ b/libs/hwui/TessellationCache.h
@@ -50,30 +50,28 @@
         enum Type {
             kNone,
             kRoundRect,
-            kAmbientShadow,
-            kSpotShadow
         };
 
         Type type;
+        float scaleX;
+        float scaleY;
         bool aa;
         SkPaint::Cap cap;
         SkPaint::Style style;
         float strokeWidth;
         union Shape {
             struct RoundRect {
-                float mScaleX;
-                float mScaleY;
-                float mWidth;
-                float mHeight;
-                float mRx;
-                float mRy;
+                float width;
+                float height;
+                float rx;
+                float ry;
             } roundRect;
         } shape;
 
         Description();
-        Description(Type type);
-        Description(Type type, const SkPaint* paint);
+        Description(Type type, const Matrix4& transform, const SkPaint& paint);
         hash_t hash() const;
+        void setupMatrixAndPaint(Matrix4* matrix, SkPaint* paint) const;
     };
 
     struct ShadowDescription {
@@ -123,12 +121,12 @@
 
     // TODO: precache/get for Oval, Lines, Points, etc.
 
-    void precacheRoundRect(const Matrix4& transform,
-            float width, float height, float rx, float ry, const SkPaint* paint) {
-        getRoundRectBuffer(transform, width, height, rx, ry, paint);
+    void precacheRoundRect(const Matrix4& transform, const SkPaint& paint,
+            float width, float height, float rx, float ry) {
+        getRoundRectBuffer(transform, paint, width, height, rx, ry);
     }
-    const VertexBuffer* getRoundRect(const Matrix4& transform,
-            float width, float height, float rx, float ry, const SkPaint* paint);
+    const VertexBuffer* getRoundRect(const Matrix4& transform, const SkPaint& paint,
+            float width, float height, float rx, float ry);
 
     void precacheShadows(const Matrix4* drawTransform, const Rect& localClip,
             bool opaque, const SkPath* casterPerimeter,
@@ -146,14 +144,14 @@
     class TessellationTask;
     class TessellationProcessor;
 
+    typedef VertexBuffer* (*Tessellator)(const Description&);
 
-    typedef VertexBuffer* (*Tessellator)(const Description&, const SkPaint&);
+    Buffer* getRectBuffer(const Matrix4& transform, const SkPaint& paint,
+            float width, float height);
+    Buffer* getRoundRectBuffer(const Matrix4& transform, const SkPaint& paint,
+            float width, float height, float rx, float ry);
 
-    Buffer* getRoundRectBuffer(const Matrix4& transform,
-            float width, float height, float rx, float ry, const SkPaint* paint);
-
-    Buffer* getOrCreateBuffer(const Description& entry,
-            Tessellator tessellator, const SkPaint* paint);
+    Buffer* getOrCreateBuffer(const Description& entry, Tessellator tessellator);
 
     uint32_t mSize;
     uint32_t mMaxSize;
diff --git a/libs/hwui/TextureCache.cpp b/libs/hwui/TextureCache.cpp
index 1001cae0..9212d0a 100644
--- a/libs/hwui/TextureCache.cpp
+++ b/libs/hwui/TextureCache.cpp
@@ -333,8 +333,7 @@
 void TextureCache::uploadLoFiTexture(bool resize, const SkBitmap* bitmap,
         uint32_t width, uint32_t height) {
     SkBitmap rgbaBitmap;
-    rgbaBitmap.setConfig(SkBitmap::kARGB_8888_Config, width, height, 0, bitmap->alphaType());
-    rgbaBitmap.allocPixels();
+    rgbaBitmap.allocPixels(SkImageInfo::MakeN32(width, height, bitmap->alphaType()));
     rgbaBitmap.eraseColor(0);
 
     SkCanvas canvas(rgbaBitmap);
diff --git a/libs/hwui/TreeInfo.h b/libs/hwui/TreeInfo.h
index 0fc0cef..249e525 100644
--- a/libs/hwui/TreeInfo.h
+++ b/libs/hwui/TreeInfo.h
@@ -16,6 +16,8 @@
 #ifndef TREEINFO_H
 #define TREEINFO_H
 
+#include <string>
+
 #include <utils/Timers.h>
 
 #include "DamageAccumulator.h"
@@ -27,6 +29,7 @@
 class BaseRenderNodeAnimator;
 class AnimationListener;
 class OpenGLRenderer;
+class RenderState;
 
 class AnimationHook {
 public:
@@ -35,6 +38,13 @@
     ~AnimationHook() {}
 };
 
+class ErrorHandler {
+public:
+    virtual void onError(const std::string& message) = 0;
+protected:
+    ~ErrorHandler() {}
+};
+
 // This would be a struct, but we want to PREVENT_COPY_AND_ASSIGN
 class TreeInfo {
     PREVENT_COPY_AND_ASSIGN(TreeInfo);
@@ -58,13 +68,15 @@
         // TODO: TRIM_MEMORY?
     };
 
-    explicit TreeInfo(TraversalMode mode)
+    explicit TreeInfo(TraversalMode mode, RenderState& renderState)
         : mode(mode)
         , frameTimeMs(0)
         , animationHook(NULL)
         , prepareTextures(mode == MODE_FULL)
         , damageAccumulator(NullDamageAccumulator::instance())
-        , renderer(0)
+        , renderState(renderState)
+        , renderer(NULL)
+        , errorHandler(NULL)
     {}
 
     const TraversalMode mode;
@@ -75,9 +87,11 @@
     bool prepareTextures;
     // Must not be null
     IDamageAccumulator* damageAccumulator;
+    RenderState& renderState;
     // The renderer that will be drawing the next frame. Use this to push any
     // layer updates or similar. May be NULL.
     OpenGLRenderer* renderer;
+    ErrorHandler* errorHandler;
 
     struct Out {
         Out()
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index dc1951b..281a8e1 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -18,332 +18,31 @@
 
 #include "CanvasContext.h"
 
-#include <cutils/properties.h>
 #include <private/hwui/DrawGlInfo.h>
 #include <strings.h>
 
+#include "EglManager.h"
 #include "RenderThread.h"
 #include "../Caches.h"
 #include "../DeferredLayerUpdater.h"
+#include "../RenderState.h"
 #include "../LayerRenderer.h"
 #include "../OpenGLRenderer.h"
 #include "../Stencil.h"
 
-#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
-#define GLES_VERSION 2
-
-// Android-specific addition that is used to show when frames began in systrace
-EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
-
 namespace android {
 namespace uirenderer {
 namespace renderthread {
 
-#define ERROR_CASE(x) case x: return #x;
-static const char* egl_error_str(EGLint error) {
-    switch (error) {
-        ERROR_CASE(EGL_SUCCESS)
-        ERROR_CASE(EGL_NOT_INITIALIZED)
-        ERROR_CASE(EGL_BAD_ACCESS)
-        ERROR_CASE(EGL_BAD_ALLOC)
-        ERROR_CASE(EGL_BAD_ATTRIBUTE)
-        ERROR_CASE(EGL_BAD_CONFIG)
-        ERROR_CASE(EGL_BAD_CONTEXT)
-        ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
-        ERROR_CASE(EGL_BAD_DISPLAY)
-        ERROR_CASE(EGL_BAD_MATCH)
-        ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
-        ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
-        ERROR_CASE(EGL_BAD_PARAMETER)
-        ERROR_CASE(EGL_BAD_SURFACE)
-        ERROR_CASE(EGL_CONTEXT_LOST)
-    default:
-        return "Unknown error";
-    }
-}
-static const char* egl_error_str() {
-    return egl_error_str(eglGetError());
-}
-
-static bool load_dirty_regions_property() {
-    char buf[PROPERTY_VALUE_MAX];
-    int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
-    return !strncasecmp("true", buf, len);
-}
-
-// This class contains the shared global EGL objects, such as EGLDisplay
-// and EGLConfig, which are re-used by CanvasContext
-class GlobalContext {
-public:
-    static GlobalContext* get();
-
-    // Returns true on success, false on failure
-    void initialize();
-
-    bool hasContext();
-
-    void usePBufferSurface();
-    EGLSurface createSurface(EGLNativeWindowType window);
-    void destroySurface(EGLSurface surface);
-
-    void destroy();
-
-    bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
-    // Returns true if the current surface changed, false if it was already current
-    bool makeCurrent(EGLSurface surface);
-    void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
-    void swapBuffers(EGLSurface surface);
-
-    bool enableDirtyRegions(EGLSurface surface);
-
-    void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
-
-private:
-    GlobalContext();
-    // GlobalContext is never destroyed, method is purposely not implemented
-    ~GlobalContext();
-
-    void loadConfig();
-    void createContext();
-    void initAtlas();
-
-    static GlobalContext* sContext;
-
-    EGLDisplay mEglDisplay;
-    EGLConfig mEglConfig;
-    EGLContext mEglContext;
-    EGLSurface mPBufferSurface;
-
-    const bool mRequestDirtyRegions;
-    bool mCanSetDirtyRegions;
-
-    EGLSurface mCurrentSurface;
-
-    sp<GraphicBuffer> mAtlasBuffer;
-    int64_t* mAtlasMap;
-    size_t mAtlasMapSize;
-};
-
-GlobalContext* GlobalContext::sContext = 0;
-
-GlobalContext* GlobalContext::get() {
-    if (!sContext) {
-        sContext = new GlobalContext();
-    }
-    return sContext;
-}
-
-GlobalContext::GlobalContext()
-        : mEglDisplay(EGL_NO_DISPLAY)
-        , mEglConfig(0)
-        , mEglContext(EGL_NO_CONTEXT)
-        , mPBufferSurface(EGL_NO_SURFACE)
-        , mRequestDirtyRegions(load_dirty_regions_property())
-        , mCurrentSurface(EGL_NO_SURFACE)
-        , mAtlasMap(NULL)
-        , mAtlasMapSize(0) {
-    mCanSetDirtyRegions = mRequestDirtyRegions;
-    ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
-}
-
-void GlobalContext::initialize() {
-    if (hasContext()) return;
-
-    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
-    LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
-            "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
-
-    EGLint major, minor;
-    LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
-            "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
-
-    ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
-
-    loadConfig();
-    createContext();
-    usePBufferSurface();
-    Caches::getInstance().init();
-    initAtlas();
-}
-
-bool GlobalContext::hasContext() {
-    return mEglDisplay != EGL_NO_DISPLAY;
-}
-
-void GlobalContext::loadConfig() {
-    EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
-    EGLint attribs[] = {
-            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
-            EGL_RED_SIZE, 8,
-            EGL_GREEN_SIZE, 8,
-            EGL_BLUE_SIZE, 8,
-            EGL_ALPHA_SIZE, 8,
-            EGL_DEPTH_SIZE, 0,
-            EGL_CONFIG_CAVEAT, EGL_NONE,
-            EGL_STENCIL_SIZE, Stencil::getStencilSize(),
-            EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
-            EGL_NONE
-    };
-
-    EGLint num_configs = 1;
-    if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
-            || num_configs != 1) {
-        // Failed to get a valid config
-        if (mCanSetDirtyRegions) {
-            ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
-            // Try again without dirty regions enabled
-            mCanSetDirtyRegions = false;
-            loadConfig();
-        } else {
-            LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
-        }
-    }
-}
-
-void GlobalContext::createContext() {
-    EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
-    mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
-    LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
-        "Failed to create context, error = %s", egl_error_str());
-}
-
-void GlobalContext::setTextureAtlas(const sp<GraphicBuffer>& buffer,
-        int64_t* map, size_t mapSize) {
-
-    // Already initialized
-    if (mAtlasBuffer.get()) {
-        ALOGW("Multiple calls to setTextureAtlas!");
-        delete map;
-        return;
-    }
-
-    mAtlasBuffer = buffer;
-    mAtlasMap = map;
-    mAtlasMapSize = mapSize;
-
-    if (hasContext()) {
-        usePBufferSurface();
-        initAtlas();
-    }
-}
-
-void GlobalContext::initAtlas() {
-    if (mAtlasBuffer.get()) {
-        Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize);
-    }
-}
-
-void GlobalContext::usePBufferSurface() {
-    LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
-            "usePBufferSurface() called on uninitialized GlobalContext!");
-
-    if (mPBufferSurface == EGL_NO_SURFACE) {
-        EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
-        mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
-    }
-    makeCurrent(mPBufferSurface);
-}
-
-EGLSurface GlobalContext::createSurface(EGLNativeWindowType window) {
-    initialize();
-    return eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
-}
-
-void GlobalContext::destroySurface(EGLSurface surface) {
-    if (isCurrent(surface)) {
-        makeCurrent(EGL_NO_SURFACE);
-    }
-    if (!eglDestroySurface(mEglDisplay, surface)) {
-        ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
-    }
-}
-
-void GlobalContext::destroy() {
-    if (mEglDisplay == EGL_NO_DISPLAY) return;
-
-    usePBufferSurface();
-    if (Caches::hasInstance()) {
-        Caches::getInstance().terminate();
-    }
-
-    eglDestroyContext(mEglDisplay, mEglContext);
-    eglDestroySurface(mEglDisplay, mPBufferSurface);
-    eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    eglTerminate(mEglDisplay);
-    eglReleaseThread();
-
-    mEglDisplay = EGL_NO_DISPLAY;
-    mEglContext = EGL_NO_CONTEXT;
-    mPBufferSurface = EGL_NO_SURFACE;
-    mCurrentSurface = EGL_NO_SURFACE;
-}
-
-bool GlobalContext::makeCurrent(EGLSurface surface) {
-    if (isCurrent(surface)) return false;
-
-    if (surface == EGL_NO_SURFACE) {
-        // If we are setting EGL_NO_SURFACE we don't care about any of the potential
-        // return errors, which would only happen if mEglDisplay had already been
-        // destroyed in which case the current context is already NO_CONTEXT
-        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-    } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
-        LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
-                (void*)surface, egl_error_str());
-    }
-    mCurrentSurface = surface;
-    return true;
-}
-
-void GlobalContext::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
-    LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
-            "Tried to beginFrame on EGL_NO_SURFACE!");
-    makeCurrent(surface);
-    if (width) {
-        eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
-    }
-    if (height) {
-        eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
-    }
-    eglBeginFrame(mEglDisplay, surface);
-}
-
-void GlobalContext::swapBuffers(EGLSurface surface) {
-    eglSwapBuffers(mEglDisplay, surface);
-    EGLint err = eglGetError();
-    LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS,
-            "Encountered EGL error %d %s during rendering", err, egl_error_str(err));
-}
-
-bool GlobalContext::enableDirtyRegions(EGLSurface surface) {
-    if (!mRequestDirtyRegions) return false;
-
-    if (mCanSetDirtyRegions) {
-        if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) {
-            ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
-                    (void*) surface, egl_error_str());
-            return false;
-        }
-        return true;
-    }
-    // Perhaps it is already enabled?
-    EGLint value;
-    if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) {
-        ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
-                (void*) surface, egl_error_str());
-        return false;
-    }
-    return value == EGL_BUFFER_PRESERVED;
-}
-
-CanvasContext::CanvasContext(bool translucent, RenderNode* rootRenderNode)
-        : mRenderThread(RenderThread::getInstance())
+CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode)
+        : mRenderThread(thread)
+        , mEglManager(thread.eglManager())
         , mEglSurface(EGL_NO_SURFACE)
         , mDirtyRegionsEnabled(false)
         , mOpaque(!translucent)
-        , mCanvas(0)
+        , mCanvas(NULL)
         , mHaveNewSurface(false)
         , mRootRenderNode(rootRenderNode) {
-    mGlobalContext = GlobalContext::get();
 }
 
 CanvasContext::~CanvasContext() {
@@ -363,19 +62,16 @@
     mNativeWindow = window;
 
     if (mEglSurface != EGL_NO_SURFACE) {
-        mGlobalContext->destroySurface(mEglSurface);
+        mEglManager.destroySurface(mEglSurface);
         mEglSurface = EGL_NO_SURFACE;
     }
 
     if (window) {
-        mEglSurface = mGlobalContext->createSurface(window);
-        LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
-                "Failed to create EGLSurface for window %p, eglErr = %s",
-                (void*) window, egl_error_str());
+        mEglSurface = mEglManager.createSurface(window);
     }
 
     if (mEglSurface != EGL_NO_SURFACE) {
-        mDirtyRegionsEnabled = mGlobalContext->enableDirtyRegions(mEglSurface);
+        mDirtyRegionsEnabled = mEglManager.enableDirtyRegions(mEglSurface);
         mHaveNewSurface = true;
         makeCurrent();
     } else {
@@ -384,7 +80,7 @@
 }
 
 void CanvasContext::swapBuffers() {
-    mGlobalContext->swapBuffers(mEglSurface);
+    mEglManager.swapBuffers(mEglSurface);
     mHaveNewSurface = false;
 }
 
@@ -397,7 +93,7 @@
 bool CanvasContext::initialize(ANativeWindow* window) {
     if (mCanvas) return false;
     setSurface(window);
-    mCanvas = new OpenGLRenderer();
+    mCanvas = new OpenGLRenderer(mRenderThread.renderState());
     mCanvas->initProperties();
     return true;
 }
@@ -424,7 +120,7 @@
 void CanvasContext::makeCurrent() {
     // TODO: Figure out why this workaround is needed, see b/13913604
     // In the meantime this matches the behavior of GLRenderer, so it is not a regression
-    mHaveNewSurface |= mGlobalContext->makeCurrent(mEglSurface);
+    mHaveNewSurface |= mEglManager.makeCurrent(mEglSurface);
 }
 
 void CanvasContext::processLayerUpdate(DeferredLayerUpdater* layerUpdater, TreeInfo& info) {
@@ -452,9 +148,7 @@
     info.out.canDrawThisFrame = !runningBehind;
 
     if (info.out.hasAnimations || !info.out.canDrawThisFrame) {
-        if (info.out.hasFunctors) {
-            info.out.requiresUiRedraw = true;
-        } else if (!info.out.requiresUiRedraw) {
+        if (!info.out.requiresUiRedraw) {
             // If animationsNeedsRedraw is set don't bother posting for an RT anim
             // as we will just end up fighting the UI thread.
             mRenderThread.postFrameCallback(this);
@@ -469,7 +163,7 @@
 
 void CanvasContext::draw() {
     LOG_ALWAYS_FATAL_IF(!mCanvas || mEglSurface == EGL_NO_SURFACE,
-            "drawDisplayList called on a context with no canvas or surface!");
+            "drawRenderNode called on a context with no canvas or surface!");
 
     profiler().markPlaybackStart();
 
@@ -477,7 +171,7 @@
     mDamageAccumulator.finish(&dirty);
 
     EGLint width, height;
-    mGlobalContext->beginFrame(mEglSurface, &width, &height);
+    mEglManager.beginFrame(mEglSurface, &width, &height);
     if (width != mCanvas->getViewportWidth() || height != mCanvas->getViewportHeight()) {
         mCanvas->setViewport(width, height);
         dirty.setEmpty();
@@ -496,7 +190,7 @@
     }
 
     Rect outBounds;
-    status |= mCanvas->drawDisplayList(mRootRenderNode.get(), outBounds);
+    status |= mCanvas->drawRenderNode(mRootRenderNode.get(), outBounds);
 
     profiler().draw(mCanvas);
 
@@ -521,7 +215,7 @@
 
     profiler().startFrame();
 
-    TreeInfo info(TreeInfo::MODE_RT_ONLY);
+    TreeInfo info(TreeInfo::MODE_RT_ONLY, mRenderThread.renderState());
     info.prepareTextures = false;
 
     prepareTree(info);
@@ -530,32 +224,26 @@
     }
 }
 
-void CanvasContext::invokeFunctor(Functor* functor) {
+void CanvasContext::invokeFunctor(RenderThread& thread, Functor* functor) {
     ATRACE_CALL();
     DrawGlInfo::Mode mode = DrawGlInfo::kModeProcessNoContext;
-    if (mGlobalContext->hasContext()) {
-        requireGlContext();
+    if (thread.eglManager().hasEglContext()) {
+        thread.eglManager().requireGlContext();
         mode = DrawGlInfo::kModeProcess;
     }
 
-    if (mCanvas) {
-        mCanvas->interrupt();
-    }
-    (*functor)(mode, NULL);
-    if (mCanvas) {
-        mCanvas->resume();
-    }
+    thread.renderState().invokeFunctor(functor, mode, NULL);
 }
 
 bool CanvasContext::copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap) {
     requireGlContext();
-    TreeInfo info(TreeInfo::MODE_FULL);
+    TreeInfo info(TreeInfo::MODE_FULL, mRenderThread.renderState());
     layer->apply(info);
-    return LayerRenderer::copyLayer(layer->backingLayer(), bitmap);
+    return LayerRenderer::copyLayer(mRenderThread.renderState(), layer->backingLayer(), bitmap);
 }
 
 void CanvasContext::flushCaches(Caches::FlushMode flushMode) {
-    if (mGlobalContext->hasContext()) {
+    if (mEglManager.hasEglContext()) {
         requireGlContext();
         Caches::getInstance().flush(flushMode);
     }
@@ -568,25 +256,25 @@
 
 Layer* CanvasContext::createRenderLayer(int width, int height) {
     requireSurface();
-    return LayerRenderer::createRenderLayer(width, height);
+    return LayerRenderer::createRenderLayer(mRenderThread.renderState(), width, height);
 }
 
 Layer* CanvasContext::createTextureLayer() {
     requireSurface();
-    return LayerRenderer::createTextureLayer();
+    return LayerRenderer::createTextureLayer(mRenderThread.renderState());
 }
 
 void CanvasContext::requireGlContext() {
     if (mEglSurface != EGL_NO_SURFACE) {
         makeCurrent();
     } else {
-        mGlobalContext->usePBufferSurface();
+        mEglManager.usePBufferSurface();
     }
 }
 
-void CanvasContext::setTextureAtlas(const sp<GraphicBuffer>& buffer,
-        int64_t* map, size_t mapSize) {
-    GlobalContext::get()->setTextureAtlas(buffer, map, mapSize);
+void CanvasContext::setTextureAtlas(RenderThread& thread,
+        const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize) {
+    thread.eglManager().setTextureAtlas(buffer, map, mapSize);
 }
 
 } /* namespace renderthread */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index d926b38..d2ce1a6 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -41,13 +41,13 @@
 
 namespace renderthread {
 
-class GlobalContext;
+class EglManager;
 
 // This per-renderer class manages the bridge between the global EGL context
 // and the render surface.
 class CanvasContext : public IFrameCallback {
 public:
-    CanvasContext(bool translucent, RenderNode* rootRenderNode);
+    CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode);
     virtual ~CanvasContext();
 
     bool initialize(ANativeWindow* window);
@@ -68,15 +68,15 @@
 
     void flushCaches(Caches::FlushMode flushMode);
 
-    void invokeFunctor(Functor* functor);
+    static void invokeFunctor(RenderThread& thread, Functor* functor);
 
     void runWithGlContext(RenderTask* task);
 
     Layer* createRenderLayer(int width, int height);
     Layer* createTextureLayer();
 
-    ANDROID_API static void setTextureAtlas(const sp<GraphicBuffer>& buffer,
-            int64_t* map, size_t mapSize);
+    ANDROID_API static void setTextureAtlas(RenderThread& thread,
+            const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
 
     void notifyFramePending();
 
@@ -91,8 +91,8 @@
 
     void requireGlContext();
 
-    GlobalContext* mGlobalContext;
     RenderThread& mRenderThread;
+    EglManager& mEglManager;
     sp<ANativeWindow> mNativeWindow;
     EGLSurface mEglSurface;
     bool mDirtyRegionsEnabled;
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 797566f..fddffd5 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -98,7 +98,7 @@
     bool canUnblockUiThread;
     bool canDrawThisFrame;
     {
-        TreeInfo info(TreeInfo::MODE_FULL);
+        TreeInfo info(TreeInfo::MODE_FULL, mRenderThread->renderState());
         canUnblockUiThread = syncFrameState(info);
         canDrawThisFrame = info.out.canDrawThisFrame;
     }
@@ -138,7 +138,7 @@
         }
     }
     // If prepareTextures is false, we ran out of texture cache space
-    return !info.out.hasFunctors && info.prepareTextures;
+    return info.prepareTextures;
 }
 
 void DrawFrameTask::unblockUiThread() {
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 96f0add..243cc5d 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -41,7 +41,7 @@
 
 enum SyncResult {
     kSync_OK = 0,
-    kSync_UIRedrawRequired = 1 << 1,
+    kSync_UIRedrawRequired = 1 << 0,
 };
 
 /*
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
new file mode 100644
index 0000000..05ca34d
--- /dev/null
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#define LOG_TAG "EglContext"
+
+#include "EglManager.h"
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include "../RenderState.h"
+#include "RenderThread.h"
+
+#define PROPERTY_RENDER_DIRTY_REGIONS "debug.hwui.render_dirty_regions"
+#define GLES_VERSION 2
+
+// Android-specific addition that is used to show when frames began in systrace
+EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+#define ERROR_CASE(x) case x: return #x;
+static const char* egl_error_str(EGLint error) {
+    switch (error) {
+        ERROR_CASE(EGL_SUCCESS)
+        ERROR_CASE(EGL_NOT_INITIALIZED)
+        ERROR_CASE(EGL_BAD_ACCESS)
+        ERROR_CASE(EGL_BAD_ALLOC)
+        ERROR_CASE(EGL_BAD_ATTRIBUTE)
+        ERROR_CASE(EGL_BAD_CONFIG)
+        ERROR_CASE(EGL_BAD_CONTEXT)
+        ERROR_CASE(EGL_BAD_CURRENT_SURFACE)
+        ERROR_CASE(EGL_BAD_DISPLAY)
+        ERROR_CASE(EGL_BAD_MATCH)
+        ERROR_CASE(EGL_BAD_NATIVE_PIXMAP)
+        ERROR_CASE(EGL_BAD_NATIVE_WINDOW)
+        ERROR_CASE(EGL_BAD_PARAMETER)
+        ERROR_CASE(EGL_BAD_SURFACE)
+        ERROR_CASE(EGL_CONTEXT_LOST)
+    default:
+        return "Unknown error";
+    }
+}
+static const char* egl_error_str() {
+    return egl_error_str(eglGetError());
+}
+
+static bool load_dirty_regions_property() {
+    char buf[PROPERTY_VALUE_MAX];
+    int len = property_get(PROPERTY_RENDER_DIRTY_REGIONS, buf, "true");
+    return !strncasecmp("true", buf, len);
+}
+
+EglManager::EglManager(RenderThread& thread)
+        : mRenderThread(thread)
+        , mEglDisplay(EGL_NO_DISPLAY)
+        , mEglConfig(0)
+        , mEglContext(EGL_NO_CONTEXT)
+        , mPBufferSurface(EGL_NO_SURFACE)
+        , mRequestDirtyRegions(load_dirty_regions_property())
+        , mCurrentSurface(EGL_NO_SURFACE)
+        , mAtlasMap(NULL)
+        , mAtlasMapSize(0) {
+    mCanSetDirtyRegions = mRequestDirtyRegions;
+    ALOGD("Render dirty regions requested: %s", mRequestDirtyRegions ? "true" : "false");
+}
+
+void EglManager::initialize() {
+    if (hasEglContext()) return;
+
+    mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
+    LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+            "Failed to get EGL_DEFAULT_DISPLAY! err=%s", egl_error_str());
+
+    EGLint major, minor;
+    LOG_ALWAYS_FATAL_IF(eglInitialize(mEglDisplay, &major, &minor) == EGL_FALSE,
+            "Failed to initialize display %p! err=%s", mEglDisplay, egl_error_str());
+
+    ALOGI("Initialized EGL, version %d.%d", (int)major, (int)minor);
+
+    loadConfig();
+    createContext();
+    usePBufferSurface();
+    mRenderThread.renderState().onGLContextCreated();
+    initAtlas();
+}
+
+bool EglManager::hasEglContext() {
+    return mEglDisplay != EGL_NO_DISPLAY;
+}
+
+void EglManager::requireGlContext() {
+    LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY, "No EGL context");
+
+    // We don't care *WHAT* surface is active, just that one is active to give
+    // us access to the GL context
+    if (mCurrentSurface == EGL_NO_SURFACE) {
+        usePBufferSurface();
+    }
+}
+
+void EglManager::loadConfig() {
+    EGLint swapBehavior = mCanSetDirtyRegions ? EGL_SWAP_BEHAVIOR_PRESERVED_BIT : 0;
+    EGLint attribs[] = {
+            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
+            EGL_RED_SIZE, 8,
+            EGL_GREEN_SIZE, 8,
+            EGL_BLUE_SIZE, 8,
+            EGL_ALPHA_SIZE, 8,
+            EGL_DEPTH_SIZE, 0,
+            EGL_CONFIG_CAVEAT, EGL_NONE,
+            EGL_STENCIL_SIZE, Stencil::getStencilSize(),
+            EGL_SURFACE_TYPE, EGL_WINDOW_BIT | swapBehavior,
+            EGL_NONE
+    };
+
+    EGLint num_configs = 1;
+    if (!eglChooseConfig(mEglDisplay, attribs, &mEglConfig, num_configs, &num_configs)
+            || num_configs != 1) {
+        // Failed to get a valid config
+        if (mCanSetDirtyRegions) {
+            ALOGW("Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...");
+            // Try again without dirty regions enabled
+            mCanSetDirtyRegions = false;
+            loadConfig();
+        } else {
+            LOG_ALWAYS_FATAL("Failed to choose config, error = %s", egl_error_str());
+        }
+    }
+}
+
+void EglManager::createContext() {
+    EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
+    mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
+    LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
+        "Failed to create context, error = %s", egl_error_str());
+}
+
+void EglManager::setTextureAtlas(const sp<GraphicBuffer>& buffer,
+        int64_t* map, size_t mapSize) {
+
+    // Already initialized
+    if (mAtlasBuffer.get()) {
+        ALOGW("Multiple calls to setTextureAtlas!");
+        delete map;
+        return;
+    }
+
+    mAtlasBuffer = buffer;
+    mAtlasMap = map;
+    mAtlasMapSize = mapSize;
+
+    if (hasEglContext()) {
+        usePBufferSurface();
+        initAtlas();
+    }
+}
+
+void EglManager::initAtlas() {
+    if (mAtlasBuffer.get()) {
+        Caches::getInstance().assetAtlas.init(mAtlasBuffer, mAtlasMap, mAtlasMapSize);
+    }
+}
+
+void EglManager::usePBufferSurface() {
+    LOG_ALWAYS_FATAL_IF(mEglDisplay == EGL_NO_DISPLAY,
+            "usePBufferSurface() called on uninitialized GlobalContext!");
+
+    if (mPBufferSurface == EGL_NO_SURFACE) {
+        EGLint attribs[] = { EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE };
+        mPBufferSurface = eglCreatePbufferSurface(mEglDisplay, mEglConfig, attribs);
+    }
+    makeCurrent(mPBufferSurface);
+}
+
+EGLSurface EglManager::createSurface(EGLNativeWindowType window) {
+    initialize();
+    EGLSurface surface = eglCreateWindowSurface(mEglDisplay, mEglConfig, window, NULL);
+    LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
+            "Failed to create EGLSurface for window %p, eglErr = %s",
+            (void*) window, egl_error_str());
+    return surface;
+}
+
+void EglManager::destroySurface(EGLSurface surface) {
+    if (isCurrent(surface)) {
+        makeCurrent(EGL_NO_SURFACE);
+    }
+    if (!eglDestroySurface(mEglDisplay, surface)) {
+        ALOGW("Failed to destroy surface %p, error=%s", (void*)surface, egl_error_str());
+    }
+}
+
+void EglManager::destroy() {
+    if (mEglDisplay == EGL_NO_DISPLAY) return;
+
+    usePBufferSurface();
+    if (Caches::hasInstance()) {
+        Caches::getInstance().terminate();
+    }
+
+    eglDestroyContext(mEglDisplay, mEglContext);
+    eglDestroySurface(mEglDisplay, mPBufferSurface);
+    eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    eglTerminate(mEglDisplay);
+    eglReleaseThread();
+
+    mEglDisplay = EGL_NO_DISPLAY;
+    mEglContext = EGL_NO_CONTEXT;
+    mPBufferSurface = EGL_NO_SURFACE;
+    mCurrentSurface = EGL_NO_SURFACE;
+}
+
+bool EglManager::makeCurrent(EGLSurface surface) {
+    if (isCurrent(surface)) return false;
+
+    if (surface == EGL_NO_SURFACE) {
+        // If we are setting EGL_NO_SURFACE we don't care about any of the potential
+        // return errors, which would only happen if mEglDisplay had already been
+        // destroyed in which case the current context is already NO_CONTEXT
+        eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+    } else if (!eglMakeCurrent(mEglDisplay, surface, surface, mEglContext)) {
+        LOG_ALWAYS_FATAL("Failed to make current on surface %p, error=%s",
+                (void*)surface, egl_error_str());
+    }
+    mCurrentSurface = surface;
+    return true;
+}
+
+void EglManager::beginFrame(EGLSurface surface, EGLint* width, EGLint* height) {
+    LOG_ALWAYS_FATAL_IF(surface == EGL_NO_SURFACE,
+            "Tried to beginFrame on EGL_NO_SURFACE!");
+    makeCurrent(surface);
+    if (width) {
+        eglQuerySurface(mEglDisplay, surface, EGL_WIDTH, width);
+    }
+    if (height) {
+        eglQuerySurface(mEglDisplay, surface, EGL_HEIGHT, height);
+    }
+    eglBeginFrame(mEglDisplay, surface);
+}
+
+void EglManager::swapBuffers(EGLSurface surface) {
+    eglSwapBuffers(mEglDisplay, surface);
+    EGLint err = eglGetError();
+    LOG_ALWAYS_FATAL_IF(err != EGL_SUCCESS,
+            "Encountered EGL error %d %s during rendering", err, egl_error_str(err));
+}
+
+bool EglManager::enableDirtyRegions(EGLSurface surface) {
+    if (!mRequestDirtyRegions) return false;
+
+    if (mCanSetDirtyRegions) {
+        if (!eglSurfaceAttrib(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, EGL_BUFFER_PRESERVED)) {
+            ALOGW("Failed to set EGL_SWAP_BEHAVIOR on surface %p, error=%s",
+                    (void*) surface, egl_error_str());
+            return false;
+        }
+        return true;
+    }
+    // Perhaps it is already enabled?
+    EGLint value;
+    if (!eglQuerySurface(mEglDisplay, surface, EGL_SWAP_BEHAVIOR, &value)) {
+        ALOGW("Failed to query EGL_SWAP_BEHAVIOR on surface %p, error=%p",
+                (void*) surface, egl_error_str());
+        return false;
+    }
+    return value == EGL_BUFFER_PRESERVED;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/EglManager.h b/libs/hwui/renderthread/EglManager.h
new file mode 100644
index 0000000..a844cfc
--- /dev/null
+++ b/libs/hwui/renderthread/EglManager.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#ifndef EGLMANAGER_H
+#define EGLMANAGER_H
+
+#include <cutils/compiler.h>
+#include <EGL/egl.h>
+#include <ui/GraphicBuffer.h>
+#include <utils/StrongPointer.h>
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+class RenderThread;
+
+// This class contains the shared global EGL objects, such as EGLDisplay
+// and EGLConfig, which are re-used by CanvasContext
+class EglManager {
+public:
+    // Returns true on success, false on failure
+    void initialize();
+
+    bool hasEglContext();
+    void requireGlContext();
+
+    void usePBufferSurface();
+    EGLSurface createSurface(EGLNativeWindowType window);
+    void destroySurface(EGLSurface surface);
+
+    void destroy();
+
+    bool isCurrent(EGLSurface surface) { return mCurrentSurface == surface; }
+    // Returns true if the current surface changed, false if it was already current
+    bool makeCurrent(EGLSurface surface);
+    void beginFrame(EGLSurface surface, EGLint* width, EGLint* height);
+    void swapBuffers(EGLSurface surface);
+
+    bool enableDirtyRegions(EGLSurface surface);
+
+    void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t mapSize);
+
+private:
+    friend class RenderThread;
+
+    EglManager(RenderThread& thread);
+    // EglContext is never destroyed, method is purposely not implemented
+    ~EglManager();
+
+    void loadConfig();
+    void createContext();
+    void initAtlas();
+
+    RenderThread& mRenderThread;
+
+    EGLDisplay mEglDisplay;
+    EGLConfig mEglConfig;
+    EGLContext mEglContext;
+    EGLSurface mPBufferSurface;
+
+    const bool mRequestDirtyRegions;
+    bool mCanSetDirtyRegions;
+
+    EGLSurface mCurrentSurface;
+
+    sp<GraphicBuffer> mAtlasBuffer;
+    int64_t* mAtlasMap;
+    size_t mAtlasMapSize;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
+
+#endif /* EGLMANAGER_H */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 4988f19..f90a26a 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -52,8 +52,8 @@
     MethodInvokeRenderTask* task = new MethodInvokeRenderTask((RunnableMethod) Bridge_ ## method); \
     ARGS(method) *args = (ARGS(method) *) task->payload()
 
-CREATE_BRIDGE2(createContext, bool translucent, RenderNode* rootRenderNode) {
-    return new CanvasContext(args->translucent, args->rootRenderNode);
+CREATE_BRIDGE3(createContext, RenderThread* thread, bool translucent, RenderNode* rootRenderNode) {
+    return new CanvasContext(*args->thread, args->translucent, args->rootRenderNode);
 }
 
 RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode)
@@ -62,6 +62,7 @@
     SETUP_TASK(createContext);
     args->translucent = translucent;
     args->rootRenderNode = rootRenderNode;
+    args->thread = &mRenderThread;
     mContext = (CanvasContext*) postAndWait(task);
     mDrawFrameTask.setContext(&mRenderThread, mContext);
 }
@@ -199,20 +200,29 @@
     postAndWait(task);
 }
 
-CREATE_BRIDGE2(invokeFunctor, CanvasContext* context, Functor* functor) {
-    args->context->invokeFunctor(args->functor);
+CREATE_BRIDGE2(invokeFunctor, RenderThread* thread, Functor* functor) {
+    CanvasContext::invokeFunctor(*args->thread, args->functor);
     return NULL;
 }
 
 void RenderProxy::invokeFunctor(Functor* functor, bool waitForCompletion) {
     ATRACE_CALL();
+    RenderThread& thread = RenderThread::getInstance();
     SETUP_TASK(invokeFunctor);
-    args->context = mContext;
+    args->thread = &thread;
     args->functor = functor;
     if (waitForCompletion) {
-        postAndWait(task);
+        // waitForCompletion = true is expected to be fairly rare and only
+        // happen in destruction. Thus it should be fine to temporarily
+        // create a Mutex
+        Mutex mutex;
+        Condition condition;
+        SignalingRenderTask syncTask(task, &mutex, &condition);
+        AutoMutex _lock(mutex);
+        thread.queue(&syncTask);
+        condition.wait(mutex);
     } else {
-        post(task);
+        thread.queue(task);
     }
 }
 
@@ -233,7 +243,7 @@
     return NULL;
 }
 
-static void enqueueDestroyLayer(Layer* layer) {
+void RenderProxy::enqueueDestroyLayer(Layer* layer) {
     SETUP_TASK(destroyLayer);
     args->layer = layer;
     RenderThread::getInstance().queue(task);
@@ -242,7 +252,7 @@
 CREATE_BRIDGE3(createDisplayListLayer, CanvasContext* context, int width, int height) {
     Layer* layer = args->context->createRenderLayer(args->width, args->height);
     if (!layer) return 0;
-    return new DeferredLayerUpdater(layer, enqueueDestroyLayer);
+    return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer);
 }
 
 DeferredLayerUpdater* RenderProxy::createDisplayListLayer(int width, int height) {
@@ -258,7 +268,7 @@
 CREATE_BRIDGE1(createTextureLayer, CanvasContext* context) {
     Layer* layer = args->context->createTextureLayer();
     if (!layer) return 0;
-    return new DeferredLayerUpdater(layer, enqueueDestroyLayer);
+    return new DeferredLayerUpdater(layer, RenderProxy::enqueueDestroyLayer);
 }
 
 DeferredLayerUpdater* RenderProxy::createTextureLayer() {
@@ -336,6 +346,22 @@
     postAndWait(task);
 }
 
+CREATE_BRIDGE4(setTextureAtlas, RenderThread* thread, GraphicBuffer* buffer, int64_t* map, size_t size) {
+    CanvasContext::setTextureAtlas(*args->thread, args->buffer, args->map, args->size);
+    args->buffer->decStrong(0);
+    return NULL;
+}
+
+void RenderProxy::setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size) {
+    SETUP_TASK(setTextureAtlas);
+    args->thread = &mRenderThread;
+    args->buffer = buffer.get();
+    args->buffer->incStrong(0);
+    args->map = map;
+    args->size = size;
+    post(task);
+}
+
 void RenderProxy::post(RenderTask* task) {
     mRenderThread.queue(task);
 }
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index a95f8f0..df0aff0 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -73,10 +73,11 @@
             float density);
     ANDROID_API void destroyCanvasAndSurface();
 
-    ANDROID_API void invokeFunctor(Functor* functor, bool waitForCompletion);
+    ANDROID_API static void invokeFunctor(Functor* functor, bool waitForCompletion);
 
     ANDROID_API void runWithGlContext(RenderTask* task);
 
+    static void enqueueDestroyLayer(Layer* layer);
     ANDROID_API DeferredLayerUpdater* createDisplayListLayer(int width, int height);
     ANDROID_API DeferredLayerUpdater* createTextureLayer();
     ANDROID_API bool copyLayerInto(DeferredLayerUpdater* layer, SkBitmap* bitmap);
@@ -90,6 +91,8 @@
 
     ANDROID_API void dumpProfileInfo(int fd);
 
+    ANDROID_API void setTextureAtlas(const sp<GraphicBuffer>& buffer, int64_t* map, size_t size);
+
 private:
     RenderThread& mRenderThread;
     CanvasContext* mContext;
diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp
index 4a4e254..03e98d5 100644
--- a/libs/hwui/renderthread/RenderThread.cpp
+++ b/libs/hwui/renderthread/RenderThread.cpp
@@ -21,7 +21,9 @@
 #include <gui/DisplayEventReceiver.h>
 #include <utils/Log.h>
 
+#include "../RenderState.h"
 #include "CanvasContext.h"
+#include "EglManager.h"
 #include "RenderProxy.h"
 
 namespace android {
@@ -138,13 +140,16 @@
         , mDisplayEventReceiver(0)
         , mVsyncRequested(false)
         , mFrameCallbackTaskPending(false)
-        , mFrameCallbackTask(0) {
+        , mFrameCallbackTask(0)
+        , mRenderState(NULL)
+        , mEglManager(NULL) {
     mFrameCallbackTask = new DispatchFrameCallbacks(this);
     mLooper = new Looper(false);
     run("RenderThread");
 }
 
 RenderThread::~RenderThread() {
+    LOG_ALWAYS_FATAL("Can't destroy the render thread");
 }
 
 void RenderThread::initializeDisplayEventReceiver() {
@@ -159,6 +164,12 @@
             Looper::EVENT_INPUT, RenderThread::displayEventReceiverCallback, this);
 }
 
+void RenderThread::initThreadLocals() {
+    initializeDisplayEventReceiver();
+    mEglManager = new EglManager(*this);
+    mRenderState = new RenderState();
+}
+
 int RenderThread::displayEventReceiverCallback(int fd, int events, void* data) {
     if (events & (Looper::EVENT_ERROR | Looper::EVENT_HANGUP)) {
         ALOGE("Display event receiver pipe was closed or an error occurred.  "
@@ -233,7 +244,7 @@
 }
 
 bool RenderThread::threadLoop() {
-    initializeDisplayEventReceiver();
+    initThreadLocals();
 
     int timeoutMillis = -1;
     for (;;) {
diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h
index 4412584..0b91e9d 100644
--- a/libs/hwui/renderthread/RenderThread.h
+++ b/libs/hwui/renderthread/RenderThread.h
@@ -31,12 +31,18 @@
 #include "TimeLord.h"
 
 namespace android {
+
 class DisplayEventReceiver;
 
 namespace uirenderer {
+
+class RenderState;
+
 namespace renderthread {
 
 class DispatchFrameCallbacks;
+class EglManager;
+class RenderProxy;
 
 class TaskQueue {
 public:
@@ -62,7 +68,7 @@
     ~IFrameCallback() {}
 };
 
-class ANDROID_API RenderThread : public Thread, public Singleton<RenderThread> {
+class ANDROID_API RenderThread : public Thread, protected Singleton<RenderThread> {
 public:
     // RenderThread takes complete ownership of tasks that are queued
     // and will delete them after they are run
@@ -79,6 +85,8 @@
     void pushBackFrameCallback(IFrameCallback* callback);
 
     TimeLord& timeLord() { return mTimeLord; }
+    RenderState& renderState() { return *mRenderState; }
+    EglManager& eglManager() { return *mEglManager; }
 
 protected:
     virtual bool threadLoop();
@@ -86,10 +94,12 @@
 private:
     friend class Singleton<RenderThread>;
     friend class DispatchFrameCallbacks;
+    friend class RenderProxy;
 
     RenderThread();
     virtual ~RenderThread();
 
+    void initThreadLocals();
     void initializeDisplayEventReceiver();
     static int displayEventReceiverCallback(int fd, int events, void* data);
     void drainDisplayEventQueue(bool skipCallbacks = false);
@@ -119,6 +129,8 @@
     DispatchFrameCallbacks* mFrameCallbackTask;
 
     TimeLord mTimeLord;
+    RenderState* mRenderState;
+    EglManager* mEglManager;
 };
 
 } /* namespace renderthread */
diff --git a/libs/hwui/thread/Task.h b/libs/hwui/thread/Task.h
index 9a211a2..30b6ff2 100644
--- a/libs/hwui/thread/Task.h
+++ b/libs/hwui/thread/Task.h
@@ -17,8 +17,6 @@
 #ifndef ANDROID_HWUI_TASK_H
 #define ANDROID_HWUI_TASK_H
 
-#define ATRACE_TAG ATRACE_TAG_VIEW
-
 #include <utils/RefBase.h>
 #include <utils/Trace.h>
 
@@ -40,7 +38,7 @@
     virtual ~Task() { }
 
     T getResult() const {
-        ATRACE_NAME("waitForTask");
+        ScopedTrace tracer(ATRACE_TAG_VIEW, "waitForTask");
         return mFuture->get();
     }
 
diff --git a/libs/input/SpriteController.cpp b/libs/input/SpriteController.cpp
index 3f6ccc9..063383b 100644
--- a/libs/input/SpriteController.cpp
+++ b/libs/input/SpriteController.cpp
@@ -206,9 +206,8 @@
             } else {
                 SkBitmap surfaceBitmap;
                 ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
-                surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config,
-                        outBuffer.width, outBuffer.height, bpr);
-                surfaceBitmap.setPixels(outBuffer.bits);
+                surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height),
+                                            outBuffer.bits, bpr);
 
                 SkCanvas surfaceCanvas(surfaceBitmap);
 
diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java
index f70110c..bdd1195 100644
--- a/location/java/android/location/Location.java
+++ b/location/java/android/location/Location.java
@@ -583,7 +583,8 @@
     }
 
     /**
-     * Get the altitude if available, in meters above sea level.
+     * Get the altitude if available, in meters above the WGS 84 reference
+     * ellipsoid.
      *
      * <p>If this location does not have an altitude then 0.0 is returned.
      */
@@ -592,7 +593,7 @@
     }
 
     /**
-     * Set the altitude, in meters above sea level.
+     * Set the altitude, in meters above the WGS 84 reference ellipsoid.
      *
      * <p>Following this call {@link #hasAltitude} will return true.
      */
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index 4b4be1b..bd2be1b 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -25,10 +25,10 @@
  * The AudioFormat class is used to access a number of audio format and
  * channel configuration constants. They are for instance used
  * in {@link AudioTrack} and {@link AudioRecord}.
- * 
+ *
  */
 public class AudioFormat {
-    
+
     //---------------------------------------------------------
     // Constants
     //--------------------
@@ -44,6 +44,10 @@
     public static final int ENCODING_PCM_8BIT = 3;
     /** Audio data format: single-precision floating-point per sample */
     public static final int ENCODING_PCM_FLOAT = 4;
+    /** Audio data format: AC-3 compressed */
+    public static final int ENCODING_AC3 = 5;
+    /** Audio data format: E-AC-3 compressed */
+    public static final int ENCODING_E_AC3 = 6;
 
     /** Invalid audio channel configuration */
     /** @deprecated use CHANNEL_INVALID instead  */
@@ -155,6 +159,41 @@
         case ENCODING_PCM_16BIT:
         case ENCODING_DEFAULT:
             return 2;
+        case ENCODING_PCM_FLOAT:
+            return 4;
+        case ENCODING_INVALID:
+        default:
+            throw new IllegalArgumentException("Bad audio format " + audioFormat);
+        }
+    }
+
+    /** @hide */
+    public static boolean isValidEncoding(int audioFormat)
+    {
+        switch (audioFormat) {
+        case ENCODING_PCM_8BIT:
+        case ENCODING_PCM_16BIT:
+        case ENCODING_PCM_FLOAT:
+        case ENCODING_AC3:
+        case ENCODING_E_AC3:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+    /** @hide */
+    public static boolean isEncodingLinearPcm(int audioFormat)
+    {
+        switch (audioFormat) {
+        case ENCODING_PCM_8BIT:
+        case ENCODING_PCM_16BIT:
+        case ENCODING_PCM_FLOAT:
+        case ENCODING_DEFAULT:
+            return true;
+        case ENCODING_AC3:
+        case ENCODING_E_AC3:
+            return false;
         case ENCODING_INVALID:
         default:
             throw new IllegalArgumentException("Bad audio format " + audioFormat);
@@ -234,7 +273,9 @@
          * @param encoding one of {@link AudioFormat#ENCODING_DEFAULT},
          *     {@link AudioFormat#ENCODING_PCM_8BIT},
          *     {@link AudioFormat#ENCODING_PCM_16BIT},
-         *     {@link AudioFormat#ENCODING_PCM_FLOAT}.
+         *     {@link AudioFormat#ENCODING_PCM_FLOAT},
+         *     {@link AudioFormat#ENCODING_AC3},
+         *     {@link AudioFormat#ENCODING_E_AC3}.
          * @return the same Builder instance.
          * @throws java.lang.IllegalArgumentException
          */
@@ -246,6 +287,8 @@
                 case ENCODING_PCM_8BIT:
                 case ENCODING_PCM_16BIT:
                 case ENCODING_PCM_FLOAT:
+                case ENCODING_AC3:
+                case ENCODING_E_AC3:
                     mEncoding = encoding;
                     break;
                 case ENCODING_INVALID:
@@ -309,7 +352,9 @@
         ENCODING_DEFAULT,
         ENCODING_PCM_8BIT,
         ENCODING_PCM_16BIT,
-        ENCODING_PCM_FLOAT
+        ENCODING_PCM_FLOAT,
+        ENCODING_AC3,
+        ENCODING_E_AC3
     })
     @Retention(RetentionPolicy.SOURCE)
     public @interface Encoding {}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 2f1e11e..fb19242 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -1254,11 +1254,6 @@
      * call {@link #stopBluetoothSco()} to clear the request and turn down the bluetooth connection.
      * <p>Even if a SCO connection is established, the following restrictions apply on audio
      * output streams so that they can be routed to SCO headset:
-     * <p>NOTE: up to and including API version
-     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method initiates a virtual
-     * voice call to the bluetooth headset.
-     * After API version {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} only a raw SCO audio
-     * connection is established.
      * <ul>
      *   <li> the stream type must be {@link #STREAM_VOICE_CALL} </li>
      *   <li> the format must be mono </li>
@@ -1274,6 +1269,11 @@
      * it will be ignored. Similarly, if a call is received or sent while an application
      * is using the SCO connection, the connection will be lost for the application and NOT
      * returned automatically when the call ends.
+     * <p>NOTE: up to and including API version
+     * {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR1}, this method initiates a virtual
+     * voice call to the bluetooth headset.
+     * After API version {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2} only a raw SCO audio
+     * connection is established.
      * @see #stopBluetoothSco()
      * @see #ACTION_SCO_AUDIO_STATE_UPDATED
      */
@@ -1287,13 +1287,38 @@
     }
 
     /**
+     * Start bluetooth SCO audio connection in virtual call mode.
+     * <p>Requires Permission:
+     *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
+     * <p>Similar to {@link #startBluetoothSco()} with explicit selection of virtual call mode.
+     * Telephony and communication applications (VoIP, Video Chat) should preferably select
+     * virtual call mode.
+     * Applications using voice input for search or commands should first try raw audio connection
+     * with {@link #startBluetoothSco()} and fall back to startBluetoothScoVirtualCall() in case of
+     * failure.
+     * @see #startBluetoothSco()
+     * @see #stopBluetoothSco()
+     * @see #ACTION_SCO_AUDIO_STATE_UPDATED
+     */
+    public void startBluetoothScoVirtualCall() {
+        IAudioService service = getService();
+        try {
+            service.startBluetoothScoVirtualCall(mICallBack);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Dead object in startBluetoothScoVirtualCall", e);
+        }
+    }
+
+    /**
      * Stop bluetooth SCO audio connection.
      * <p>Requires Permission:
      *   {@link android.Manifest.permission#MODIFY_AUDIO_SETTINGS}.
      * <p>This method must be called by applications having requested the use of
-     * bluetooth SCO audio with {@link #startBluetoothSco()}
-     * when finished with the SCO connection or if connection fails.
+     * bluetooth SCO audio with {@link #startBluetoothSco()} or
+     * {@link #startBluetoothScoVirtualCall()} when finished with the SCO connection or
+     * if connection fails.
      * @see #startBluetoothSco()
+     * @see #startBluetoothScoVirtualCall()
      */
     public void stopBluetoothSco(){
         IAudioService service = getService();
@@ -1616,26 +1641,6 @@
     }
 
     /**
-     * @hide
-     * If the stream is active locally or remotely, adjust its volume according to the enforced
-     * priority rules.
-     * Note: only AudioManager.STREAM_MUSIC is supported at the moment
-     */
-    public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) {
-        if (streamType != STREAM_MUSIC) {
-            Log.w(TAG, "adjustLocalOrRemoteStreamVolume() doesn't support stream " + streamType);
-        }
-        IAudioService service = getService();
-        try {
-            service.adjustLocalOrRemoteStreamVolume(streamType, direction,
-                    mContext.getOpPackageName());
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in adjustLocalOrRemoteStreamVolume", e);
-        }
-    }
-
-
-    /**
      * Return a new audio session identifier not associated with any player or effect.
      * It can for instance be used to create one of the {@link android.media.audiofx.AudioEffect}
      * objects.
@@ -2189,48 +2194,8 @@
             Log.e(TAG, "Cannot call registerMediaButtonIntent() with a null parameter");
             return;
         }
-        IAudioService service = getService();
-        try {
-            // pi != null, this is currently still needed to support across
-            // reboot launching of the last app.
-            service.registerMediaButtonIntent(pi, eventReceiver,
-                    eventReceiver == null ? mToken : null);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
-        }
         MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
-        helper.addMediaButtonListener(pi, mContext);
-    }
-
-    /**
-     * @hide
-     * Used internally by telephony package to register an intent receiver for ACTION_MEDIA_BUTTON.
-     * @param eventReceiver the component that will receive the media button key events,
-     *          no-op if eventReceiver is null
-     */
-    public void registerMediaButtonEventReceiverForCalls(ComponentName eventReceiver) {
-        if (eventReceiver == null) {
-            return;
-        }
-        IAudioService service = getService();
-        try {
-            // eventReceiver != null
-            service.registerMediaButtonEventReceiverForCalls(eventReceiver);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in registerMediaButtonEventReceiverForCalls", e);
-        }
-    }
-
-    /**
-     * @hide
-     */
-    public void unregisterMediaButtonEventReceiverForCalls() {
-        IAudioService service = getService();
-        try {
-            service.unregisterMediaButtonEventReceiverForCalls();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in unregisterMediaButtonEventReceiverForCalls", e);
-        }
+        helper.addMediaButtonListener(pi, eventReceiver, mContext);
     }
 
     /**
@@ -2267,12 +2232,6 @@
      * @hide
      */
     public void unregisterMediaButtonIntent(PendingIntent pi) {
-        IAudioService service = getService();
-        try {
-            service.unregisterMediaButtonIntent(pi);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
-        }
         MediaSessionLegacyHelper helper = MediaSessionLegacyHelper.getHelper(mContext);
         helper.removeMediaButtonListener(pi);
     }
@@ -2438,46 +2397,6 @@
     }
 
     /**
-     * @hide
-     * Request the user of a RemoteControlClient to seek to the given playback position.
-     * @param generationId the RemoteControlClient generation counter for which this request is
-     *         issued. Requests for an older generation than current one will be ignored.
-     * @param timeMs the time in ms to seek to, must be positive.
-     */
-    public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
-        if (timeMs < 0) {
-            return;
-        }
-        IAudioService service = getService();
-        try {
-            service.setRemoteControlClientPlaybackPosition(generationId, timeMs);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in setRccPlaybackPosition("+ generationId + ", "
-                    + timeMs + ")", e);
-        }
-    }
-
-    /**
-     * @hide
-     * Notify the user of a RemoteControlClient that it should update its metadata with the
-     * new value for the given key.
-     * @param generationId the RemoteControlClient generation counter for which this request is
-     *         issued. Requests for an older generation than current one will be ignored.
-     * @param key the metadata key for which a new value exists
-     * @param value the new metadata value
-     */
-    public void updateRemoteControlClientMetadata(int generationId, int key,
-            Rating value) {
-        IAudioService service = getService();
-        try {
-            service.updateRemoteControlClientMetadata(generationId, key, value);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Dead object in updateRemoteControlClientMetadata("+ generationId + ", "
-                    + key +", " + value + ")", e);
-        }
-    }
-
-    /**
      *  @hide
      *  Reload audio settings. This method is called by Settings backup
      *  agent when audio settings are restored and causes the AudioService
@@ -2534,6 +2453,9 @@
     // from AudioManager. AudioSystem is an internal class used by AudioManager and AudioService.
 
     /** @hide
+     * The audio device code for representing "no device." */
+    public static final int DEVICE_NONE = AudioSystem.DEVICE_NONE;
+    /** @hide
      *  The audio output device code for the small speaker at the front of the device used
      *  when placing calls.  Does not refer to an in-ear headphone without attached microphone,
      *  such as earbuds, earphones, or in-ear monitors (IEM). Those would be handled as a
@@ -2890,12 +2812,9 @@
      * @hide
      */
     public int getRemoteStreamVolume() {
-        try {
-            return getService().getRemoteStreamVolume();
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error getting remote stream volume", e);
-            return 0;
-        }
+        // TODO STOPSHIP switch callers to use media sessions instead
+        Log.e(TAG, "Need to implement new Remote Volume!");
+        return 0;
     }
 
     /**
@@ -2903,12 +2822,9 @@
      * @hide
      */
     public int getRemoteStreamMaxVolume() {
-        try {
-            return getService().getRemoteStreamMaxVolume();
-        } catch (RemoteException e) {
-            Log.w(TAG, "Error getting remote stream max volume", e);
-            return 0;
-        }
+        // TODO STOPSHIP switch callers to use media sessions instead
+        Log.e(TAG, "Need to implement new Remote Volume!");
+        return 0;
     }
 
     /**
@@ -2971,7 +2887,9 @@
     /** @hide
      */
     public static final int ERROR_NO_INIT = AudioSystem.NO_INIT;
-    /** @hide
+    /**
+     * An error code indicating that the object reporting it is no longer valid and needs to
+     * be recreated.
      */
     public static final int ERROR_DEAD_OBJECT = AudioSystem.DEAD_OBJECT;
 
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 72f4a58..0c224a6 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -830,24 +830,6 @@
     }
 
     /** @see AudioManager#adjustVolume(int, int) */
-    public void adjustVolume(int direction, int flags, String callingPackage) {
-        adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags,
-                callingPackage);
-    }
-
-    /** @see AudioManager#adjustLocalOrRemoteStreamVolume(int, int) with current assumption
-     *  on streamType: fixed to STREAM_MUSIC */
-    public void adjustLocalOrRemoteStreamVolume(int streamType, int direction,
-            String callingPackage) {
-        if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
-        if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
-            adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0, callingPackage);
-        } else if (mMediaFocusControl.checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
-            mMediaFocusControl.adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
-        }
-    }
-
-    /** @see AudioManager#adjustVolume(int, int) */
     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage) {
         if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
@@ -2074,7 +2056,19 @@
     }
 
     /** @see AudioManager#startBluetoothSco() */
-    public void startBluetoothSco(IBinder cb, int targetSdkVersion){
+    public void startBluetoothSco(IBinder cb, int targetSdkVersion) {
+        int scoAudioMode =
+                (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
+                        SCO_MODE_VIRTUAL_CALL : SCO_MODE_RAW;
+        startBluetoothScoInt(cb, scoAudioMode);
+    }
+
+    /** @see AudioManager#startBluetoothScoVirtualCall() */
+    public void startBluetoothScoVirtualCall(IBinder cb) {
+        startBluetoothScoInt(cb, SCO_MODE_VIRTUAL_CALL);
+    }
+
+    void startBluetoothScoInt(IBinder cb, int scoAudioMode){
         if (!checkAudioSettingsPermission("startBluetoothSco()") ||
                 !mSystemReady) {
             return;
@@ -2086,7 +2080,7 @@
         // The caller identity must be cleared after getScoClient() because it is needed if a new
         // client is created.
         final long ident = Binder.clearCallingIdentity();
-        client.incCount(targetSdkVersion);
+        client.incCount(scoAudioMode);
         Binder.restoreCallingIdentity(ident);
     }
 
@@ -2132,9 +2126,9 @@
             }
         }
 
-        public void incCount(int targetSdkVersion) {
+        public void incCount(int scoAudioMode) {
             synchronized(mScoClients) {
-                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, targetSdkVersion);
+                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, scoAudioMode);
                 if (mStartcount == 0) {
                     try {
                         mCb.linkToDeath(this, 0);
@@ -2204,7 +2198,7 @@
             }
         }
 
-        private void requestScoState(int state, int targetSdkVersion) {
+        private void requestScoState(int state, int scoAudioMode) {
             checkScoAudioState();
             if (totalCount() == 0) {
                 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
@@ -2219,9 +2213,7 @@
                                 (mScoAudioState == SCO_STATE_INACTIVE ||
                                  mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
                             if (mScoAudioState == SCO_STATE_INACTIVE) {
-                                mScoAudioMode =
-                                        (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
-                                                SCO_MODE_VIRTUAL_CALL : SCO_MODE_RAW;
+                                mScoAudioMode = scoAudioMode;
                                 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
                                     boolean status;
                                     if (mScoAudioMode == SCO_MODE_RAW) {
@@ -4420,84 +4412,12 @@
         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) {
-        mMediaFocusControl.setRemoteControlClientPlaybackPosition(generationId, timeMs);
-    }
-
-    public void updateRemoteControlClientMetadata(int generationId, int key, Rating value) {
-        mMediaFocusControl.updateRemoteControlClientMetadata(generationId, key, value);
-    }
-
-    public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
-        mMediaFocusControl.registerRemoteVolumeObserverForRcc(rccId, rvo);
-    }
-
-    @Override
-    public int getRemoteStreamVolume() {
-        return mMediaFocusControl.getRemoteStreamVolume();
-    }
-
-    @Override
-    public int getRemoteStreamMaxVolume() {
-        return mMediaFocusControl.getRemoteStreamMaxVolume();
-    }
-
     @Override
     public void setRemoteStreamVolume(int index) {
         enforceSelfOrSystemUI("set the remote stream volume");
         mMediaFocusControl.setRemoteStreamVolume(index);
     }
 
-    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) {
-        if (DEBUG_SESSIONS) {
-            int pid = getCallingPid();
-            Log.w(TAG, "Call to dispatchMediaKeyEvent from " + pid);
-        }
-        MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, false);
-    }
-
-    public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
-        if (DEBUG_SESSIONS) {
-            int pid = getCallingPid();
-            Log.w(TAG, "Call to dispatchMediaKeyEventUnderWakelock from " + pid);
-        }
-        MediaSessionLegacyHelper.getHelper(mContext).sendMediaButtonEvent(keyEvent, true);
-    }
-
     //==========================================================================================
     // Audio Focus
     //==========================================================================================
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 9fbcd18..63ed10c 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -225,6 +225,7 @@
     // audio device definitions: must be kept in sync with values in system/core/audio.h
     //
 
+    public static final int DEVICE_NONE = 0x0;
     // reserved bits
     public static final int DEVICE_BIT_IN = 0x80000000;
     public static final int DEVICE_BIT_DEFAULT = 0x40000000;
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index cfd9c3b..3a72833 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -457,25 +457,19 @@
 
         //--------------
         // audio format
-        switch (audioFormat) {
-        case AudioFormat.ENCODING_DEFAULT:
-            mAudioFormat = AudioFormat.ENCODING_PCM_16BIT;
-            break;
-        case AudioFormat.ENCODING_PCM_16BIT:
-        case AudioFormat.ENCODING_PCM_8BIT:
-        case AudioFormat.ENCODING_PCM_FLOAT:
-            mAudioFormat = audioFormat;
-            break;
-        default:
-            throw new IllegalArgumentException("Unsupported sample encoding."
-                + " Should be ENCODING_PCM_8BIT or ENCODING_PCM_16BIT"
-                + " or ENCODING_PCM_FLOAT"
-                + ".");
+        if (audioFormat == AudioFormat.ENCODING_DEFAULT) {
+            audioFormat = AudioFormat.ENCODING_PCM_16BIT;
         }
 
+        if (!AudioFormat.isValidEncoding(audioFormat)) {
+            throw new IllegalArgumentException("Unsupported audio encoding.");
+        }
+        mAudioFormat = audioFormat;
+
         //--------------
         // audio load mode
-        if ( (mode != MODE_STREAM) && (mode != MODE_STATIC) ) {
+        if (((mode != MODE_STREAM) && (mode != MODE_STATIC)) ||
+                ((mode != MODE_STREAM) && !AudioFormat.isEncodingLinearPcm(mAudioFormat))) {
             throw new IllegalArgumentException("Invalid mode.");
         }
         mDataLoadMode = mode;
@@ -522,8 +516,13 @@
     private void audioBuffSizeCheck(int audioBufferSize) {
         // NB: this section is only valid with PCM data.
         //     To update when supporting compressed formats
-        int frameSizeInBytes = mChannelCount
-                * (AudioFormat.getBytesPerSample(mAudioFormat));
+        int frameSizeInBytes;
+        if (AudioFormat.isEncodingLinearPcm(mAudioFormat)) {
+            frameSizeInBytes = mChannelCount
+                    * (AudioFormat.getBytesPerSample(mAudioFormat));
+        } else {
+            frameSizeInBytes = 1;
+        }
         if ((audioBufferSize % frameSizeInBytes != 0) || (audioBufferSize < 1)) {
             throw new IllegalArgumentException("Invalid audio buffer size.");
         }
@@ -757,9 +756,7 @@
             }
         }
 
-        if ((audioFormat != AudioFormat.ENCODING_PCM_16BIT)
-            && (audioFormat != AudioFormat.ENCODING_PCM_8BIT)
-            && (audioFormat != AudioFormat.ENCODING_PCM_FLOAT)) {
+        if (!AudioFormat.isValidEncoding(audioFormat)) {
             loge("getMinBufferSize(): Invalid audio format.");
             return ERROR_BAD_VALUE;
         }
@@ -1164,7 +1161,9 @@
      * @param sizeInBytes the number of bytes to read in audioData after the offset.
      * @return the number of bytes that were written or {@link #ERROR_INVALID_OPERATION}
      *    if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
-     *    the parameters don't resolve to valid data and indexes.
+     *    the parameters don't resolve to valid data and indexes, or
+     *    {@link AudioManager#ERROR_DEAD_OBJECT} if the AudioTrack is not valid anymore and
+     *    needs to be recreated.
      */
 
     public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) {
@@ -1213,7 +1212,7 @@
 
     public int write(short[] audioData, int offsetInShorts, int sizeInShorts) {
 
-        if (mState == STATE_UNINITIALIZED || mAudioFormat == AudioFormat.ENCODING_PCM_FLOAT) {
+        if (mState == STATE_UNINITIALIZED || mAudioFormat != AudioFormat.ENCODING_PCM_16BIT) {
             return ERROR_INVALID_OPERATION;
         }
 
@@ -1473,7 +1472,6 @@
         void onPeriodicNotification(AudioTrack track);
     }
 
-
     //---------------------------------------------------------
     // Inner classes
     //--------------------
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index c29e967..4dcdd19 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -36,13 +36,8 @@
  */
 interface IAudioService {
 
-    void adjustVolume(int direction, int flags, String callingPackage);
-
     boolean isLocalOrRemoteMusicActive();
 
-    oneway void adjustLocalOrRemoteStreamVolume(int streamType, int direction,
-            String callingPackage);
-
     void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags,
             String callingPackage);
 
@@ -127,15 +122,6 @@
 
     int getCurrentAudioFocus();
 
-    oneway void dispatchMediaKeyEvent(in KeyEvent keyEvent);
-    void dispatchMediaKeyEventUnderWakelock(in KeyEvent keyEvent);
-
-           void registerMediaButtonIntent(in PendingIntent pi, in ComponentName c, IBinder token);
-    oneway void unregisterMediaButtonIntent(in PendingIntent pi);
-
-    oneway void registerMediaButtonEventReceiverForCalls(in ComponentName c);
-    oneway void unregisterMediaButtonEventReceiverForCalls();
-
     /**
      * Register an IRemoteControlDisplay.
      * Success of registration is subject to a check on
@@ -188,43 +174,9 @@
      */
     oneway void remoteControlDisplayWantsPlaybackPositionSync(in IRemoteControlDisplay rcd,
             boolean wantsSync);
-    /**
-     * Request the user of a RemoteControlClient to seek to the given playback position.
-     * @param generationId the RemoteControlClient generation counter for which this request is
-     *         issued. Requests for an older generation than current one will be ignored.
-     * @param timeMs the time in ms to seek to, must be positive.
-     */
-     void setRemoteControlClientPlaybackPosition(int generationId, long timeMs);
-     /**
-      * Notify the user of a RemoteControlClient that it should update its metadata with the
-      * new value for the given key.
-      * @param generationId the RemoteControlClient generation counter for which this request is
-      *         issued. Requests for an older generation than current one will be ignored.
-      * @param key the metadata key for which a new value exists
-      * @param value the new metadata value
-      */
-     void updateRemoteControlClientMetadata(int generationId, int key, in Rating value);
-
-    /**
-     * Do not use directly, use instead
-     *     {@link android.media.AudioManager#registerRemoteControlClient(RemoteControlClient)}
-     */
-    int registerRemoteControlClient(in PendingIntent mediaIntent,
-            in IRemoteControlClient rcClient, in String callingPackageName);
-    /**
-     * Do not use directly, use instead
-     *     {@link android.media.AudioManager#unregisterRemoteControlClient(RemoteControlClient)}
-     */
-    oneway void unregisterRemoteControlClient(in PendingIntent mediaIntent,
-            in IRemoteControlClient rcClient);
-
-    oneway void setPlaybackInfoForRcc(int rccId, int what, int value);
-    void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed);
-           int  getRemoteStreamMaxVolume();
-           int  getRemoteStreamVolume();
-    oneway void registerRemoteVolumeObserverForRcc(int rccId, in IRemoteVolumeObserver rvo);
 
     void startBluetoothSco(IBinder cb, int targetSdkVersion);
+    void startBluetoothScoVirtualCall(IBinder cb);
     void stopBluetoothSco(IBinder cb);
 
     void forceVolumeControlStream(int streamType, IBinder cb);
diff --git a/media/java/android/media/MediaDrm.java b/media/java/android/media/MediaDrm.java
index 440653a..6559bc5 100644
--- a/media/java/android/media/MediaDrm.java
+++ b/media/java/android/media/MediaDrm.java
@@ -181,6 +181,27 @@
     }
 
     /**
+     * Thrown when an unrecoverable failure occurs during a MediaDrm operation.
+     * Extends java.lang.IllegalStateException with the addition of an error
+     * code that may be useful in diagnosing the failure.
+     */
+    public static final class MediaDrmStateException extends java.lang.IllegalStateException {
+        private final int mErrorCode;
+
+        public MediaDrmStateException(int errorCode, String detailMessage) {
+            super(detailMessage);
+            mErrorCode = errorCode;
+        }
+
+        /**
+         * Retrieve the associated error code
+         */
+        public int getErrorCode() {
+            return mErrorCode;
+        }
+    }
+
+    /**
      * Register a callback to be invoked when an event occurs
      *
      * @param listener the callback that will be run
diff --git a/media/java/android/media/MediaFocusControl.java b/media/java/android/media/MediaFocusControl.java
index 1c73c05..a4a7c4e 100644
--- a/media/java/android/media/MediaFocusControl.java
+++ b/media/java/android/media/MediaFocusControl.java
@@ -379,32 +379,11 @@
                     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 */,
-                            (PlayerRecord.RccPlaybackState)msg.obj /* newState */);
-                    break;
-
-                case MSG_RCC_SEEK_REQUEST:
-                    onSetRemoteControlClientPlaybackPosition(
-                            msg.arg1 /* generationId */, ((Long)msg.obj).longValue() /* timeMs */);
-                    break;
-
-                case MSG_RCC_UPDATE_METADATA:
-                    onUpdateRemoteControlClientMetadata(msg.arg1 /*genId*/, msg.arg2 /*key*/,
-                            (Rating) msg.obj /* value */);
-                    break;
-
                 case MSG_RCDISPLAY_INIT_INFO:
                     // msg.obj is guaranteed to be non null
                     onRcDisplayInitInfo((IRemoteControlDisplay)msg.obj /*newRcd*/,
@@ -2003,217 +1982,6 @@
         }
     }
 
-    protected void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
-        // ignore position change requests if invalid generation ID
-        synchronized(mPRStack) {
-            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(mPRStack) {
-            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 updateRemoteControlClientMetadata(int genId, int key, Rating value) {
-        sendMsg(mEventHandler, MSG_RCC_UPDATE_METADATA, SENDMSG_QUEUE,
-                genId /* arg1 */, key /* arg2 */, value /* obj */, 0 /* delay */);
-    }
-
-    private void onUpdateRemoteControlClientMetadata(int genId, int key, Rating value) {
-        if(DEBUG_RC) Log.d(TAG, "onUpdateRemoteControlClientMetadata(genId=" + genId +
-                ", what=" + key + ",rating=" + value + ")");
-        synchronized(mPRStack) {
-            synchronized(mCurrentRcLock) {
-                if ((mCurrentRcClient != null) && (mCurrentRcClientGen == genId)) {
-                    try {
-                        switch (key) {
-                            case MediaMetadataEditor.RATING_KEY_BY_USER:
-                                mCurrentRcClient.updateMetadata(genId, key, value);
-                                break;
-                            default:
-                                Log.e(TAG, "unhandled metadata key " + key + " update for RCC "
-                                        + genId);
-                                break;
-                        }
-                    } 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(mPRStack) {
-            // 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 = mPRStack.size()-1; index >= 0; index--) {
-                    final PlayerRecord prse = mPRStack.elementAt(index);
-                    if (prse.getRccId() == rccId) {
-                        switch (key) {
-                            case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
-                                prse.mPlaybackType = value;
-                                postReevaluateRemote();
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_VOLUME:
-                                prse.mPlaybackVolume = value;
-                                synchronized (mMainRemote) {
-                                    if (rccId == mMainRemote.mRccId) {
-                                        mMainRemote.mVolume = value;
-                                        mVolumeController.postHasNewRemotePlaybackInfo();
-                                    }
-                                }
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
-                                prse.mPlaybackVolumeMax = value;
-                                synchronized (mMainRemote) {
-                                    if (rccId == mMainRemote.mRccId) {
-                                        mMainRemote.mVolumeMax = value;
-                                        mVolumeController.postHasNewRemotePlaybackInfo();
-                                    }
-                                }
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
-                                prse.mPlaybackVolumeHandling = value;
-                                synchronized (mMainRemote) {
-                                    if (rccId == mMainRemote.mRccId) {
-                                        mMainRemote.mVolumeHandling = value;
-                                        mVolumeController.postHasNewRemotePlaybackInfo();
-                                    }
-                                }
-                                break;
-                            case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
-                                prse.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 mPRStack 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 PlayerRecord.RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
-    }
-
-    private void onNewPlaybackStateForRcc(int rccId, int state,
-            PlayerRecord.RccPlaybackState newState) {
-        if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
-                + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
-        synchronized(mPRStack) {
-            if (mPRStack.empty()) {
-                return;
-            }
-            PlayerRecord oldTopPrse = mPRStack.lastElement(); // top of the stack before any changes
-            PlayerRecord prse = null;
-            int lastPlayingIndex = mPRStack.size();
-            int inStackIndex = -1;
-            try {
-                // go through the stack from the top to figure out who's playing, and the position
-                // of this RemoteControlClient (note that it may not be in the stack)
-                for (int index = mPRStack.size()-1; index >= 0; index--) {
-                    prse = mPRStack.elementAt(index);
-                    if (prse.getRccId() == rccId) {
-                        inStackIndex = index;
-                        prse.mPlaybackState = newState;
-                    }
-                    if (prse.isPlaybackActive()) {
-                        lastPlayingIndex = index;
-                    }
-                }
-
-                if (inStackIndex != -1) {
-                    // is in the stack
-                    prse = mPRStack.elementAt(inStackIndex);
-                    synchronized (mMainRemote) {
-                        if (rccId == mMainRemote.mRccId) {
-                            mMainRemoteIsActive = isPlaystateActive(state);
-                            postReevaluateRemote();
-                        }
-                    }
-                    if (mPRStack.size() > 1) { // no need to remove and add if stack contains only 1
-                        // remove it from its old location in the stack
-                        mPRStack.removeElementAt(inStackIndex);
-                        if (prse.isPlaybackActive()) {
-                            // and put it at the top
-                            mPRStack.push(prse);
-                        } else {
-                            // and put it after the ones with active playback
-                            if (inStackIndex > lastPlayingIndex) {
-                                mPRStack.add(lastPlayingIndex, prse);
-                            } else {
-                                mPRStack.add(lastPlayingIndex - 1, prse);
-                            }
-                        }
-                    }
-
-                    if (oldTopPrse != mPRStack.lastElement()) {
-                        // the top of the stack changed:
-                        final ComponentName target =
-                                mPRStack.lastElement().getMediaButtonReceiver();
-                        if (target != null) {
-                            // post message to persist the default media button receiver
-                            mEventHandler.sendMessage( mEventHandler.obtainMessage(
-                                    MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
-                        }
-                        // reevaluate the display
-                        checkUpdateRemoteControlDisplay_syncPrs(RC_INFO_ALL);
-                    }
-                }
-            } catch (ArrayIndexOutOfBoundsException e) {
-                // not expected to happen, indicates improper concurrent modification or bad index
-                Log.e(TAG, "Wrong index (inStack=" + inStackIndex + " lastPlaying=" + lastPlayingIndex
-                        + " size=" + mPRStack.size()
-                        + "accessing PlayerRecord stack in onNewPlaybackStateForRcc", 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(mPRStack) {
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e25714a..d1909bc 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -1826,11 +1826,7 @@
             }
             SubtitleTrack track = mInbandSubtitleTracks[index];
             if (track != null) {
-                long runID = data.getStartTimeUs() + 1;
-                track.onData(data.getData(), true /* eos */, runID);
-                track.setRunDiscardTimeMs(
-                        runID,
-                        (data.getStartTimeUs() + data.getDurationUs()) / 1000);
+                track.onData(data);
             }
         }
     };
@@ -2339,7 +2335,9 @@
                 case MEDIA_INFO_EXTERNAL_METADATA_UPDATE:
                     msg.arg1 = MEDIA_INFO_METADATA_UPDATE;
                     // update default track selection
-                    mSubtitleController.selectDefaultTrack();
+                    if (mSubtitleController != null) {
+                        mSubtitleController.selectDefaultTrack();
+                    }
                     break;
                 }
 
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index ddd5b72..3336694 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -29,7 +29,6 @@
 import android.hardware.display.WifiDisplay;
 import android.hardware.display.WifiDisplayStatus;
 import android.media.session.MediaSession;
-import android.media.session.RemoteVolumeProvider;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Process;
@@ -2204,10 +2203,10 @@
                 return;
             }
             if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
-                int volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_FIXED;
+                int volumeControl = VolumeProvider.VOLUME_CONTROL_FIXED;
                 switch (mVolumeHandling) {
                     case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE:
-                        volumeControl = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+                        volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
                         break;
                     case RemoteControlClient.PLAYBACK_VOLUME_FIXED:
                     default:
@@ -2226,7 +2225,7 @@
             }
         }
 
-        class SessionVolumeProvider extends RemoteVolumeProvider {
+        class SessionVolumeProvider extends VolumeProvider {
 
             public SessionVolumeProvider(int volumeControl, int maxVolume) {
                 super(volumeControl, maxVolume);
diff --git a/media/java/android/media/MiniThumbFile.java b/media/java/android/media/MiniThumbFile.java
index 23c3652..664308c 100644
--- a/media/java/android/media/MiniThumbFile.java
+++ b/media/java/android/media/MiniThumbFile.java
@@ -248,7 +248,8 @@
                 long magic = mBuffer.getLong();
                 int length = mBuffer.getInt();
 
-                if (size >= 1 + 8 + 4 + length && data.length >= length) {
+                if (size >= 1 + 8 + 4 + length && length != 0 && magic != 0 && flag == 1 &&
+                        data.length >= length) {
                     mBuffer.get(data, 0, length);
                     return data;
                 }
diff --git a/media/java/android/media/RemoteController.java b/media/java/android/media/RemoteController.java
index 76c7299..1f5b216 100644
--- a/media/java/android/media/RemoteController.java
+++ b/media/java/android/media/RemoteController.java
@@ -35,6 +35,7 @@
 import android.os.Looper;
 import android.os.Message;
 import android.os.SystemClock;
+import android.os.UserHandle;
 import android.util.DisplayMetrics;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -361,18 +362,10 @@
         if (timeMs < 0) {
             throw new IllegalArgumentException("illegal negative time value");
         }
-        if (USE_SESSIONS) {
-            synchronized (mInfoLock) {
-                if (mCurrentSession != null) {
-                    mCurrentSession.getTransportControls().seekTo(timeMs);
-                }
+        synchronized (mInfoLock) {
+            if (mCurrentSession != null) {
+                mCurrentSession.getTransportControls().seekTo(timeMs);
             }
-        } else {
-            final int genId;
-            synchronized (mGenLock) {
-                genId = mClientGenerationIdCurrent;
-            }
-            mAudioManager.setRemoteControlClientPlaybackPosition(genId, timeMs);
         }
         return true;
     }
@@ -534,34 +527,15 @@
             if (!mMetadataChanged) {
                 return;
             }
-            if (USE_SESSIONS) {
-                synchronized (mInfoLock) {
-                    if (mCurrentSession != null) {
-                        if (mEditorMetadata.containsKey(
-                                String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
-                            Rating rating = (Rating) getObject(
-                                    MediaMetadataEditor.RATING_KEY_BY_USER, null);
-                            if (rating != null) {
-                                mCurrentSession.getTransportControls().setRating(rating);
-                            }
-                        }
-                    }
-                }
-            } else {
-                final int genId;
-                synchronized(mGenLock) {
-                    genId = mClientGenerationIdCurrent;
-                }
-                synchronized(mInfoLock) {
+            synchronized (mInfoLock) {
+                if (mCurrentSession != null) {
                     if (mEditorMetadata.containsKey(
                             String.valueOf(MediaMetadataEditor.RATING_KEY_BY_USER))) {
                         Rating rating = (Rating) getObject(
                                 MediaMetadataEditor.RATING_KEY_BY_USER, null);
-                        mAudioManager.updateRemoteControlClientMetadata(genId,
-                              MediaMetadataEditor.RATING_KEY_BY_USER,
-                              rating);
-                    } else {
-                        Log.e(TAG, "no metadata to apply");
+                        if (rating != null) {
+                            mCurrentSession.getTransportControls().setRating(rating);
+                        }
                     }
                 }
             }
@@ -820,12 +794,12 @@
         final ComponentName listenerComponent = new ComponentName(mContext,
                 mOnClientUpdateListener.getClass());
         mSessionManager.addActiveSessionsListener(mSessionListener, listenerComponent,
-                ActivityManager.getCurrentUser());
+                UserHandle.myUserId());
         mSessionListener.onActiveSessionsChanged(mSessionManager
                 .getActiveSessions(listenerComponent));
         if (DEBUG) {
             Log.d(TAG, "Registered session listener with component " + listenerComponent
-                    + " for user " + ActivityManager.getCurrentUser());
+                    + " for user " + UserHandle.myUserId());
         }
     }
 
@@ -836,7 +810,7 @@
         mSessionManager.removeActiveSessionsListener(mSessionListener);
         if (DEBUG) {
             Log.d(TAG, "Unregistered session listener for user "
-                    + ActivityManager.getCurrentUser());
+                    + UserHandle.myUserId());
         }
     }
 
diff --git a/media/java/android/media/SubtitleTrack.java b/media/java/android/media/SubtitleTrack.java
index b0e182d..9fedf63 100644
--- a/media/java/android/media/SubtitleTrack.java
+++ b/media/java/android/media/SubtitleTrack.java
@@ -75,6 +75,14 @@
 
     private long mNextScheduledTimeMs = -1;
 
+    protected void onData(SubtitleData data) {
+        long runID = data.getStartTimeUs() + 1;
+        onData(data.getData(), true /* eos */, runID);
+        setRunDiscardTimeMs(
+                runID,
+                (data.getStartTimeUs() + data.getDurationUs()) / 1000);
+    }
+
     /**
      * Called when there is input data for the subtitle track.  The
      * complete subtitle for a track can include multiple whole units
diff --git a/media/java/android/media/session/RemoteVolumeProvider.java b/media/java/android/media/VolumeProvider.java
similarity index 92%
rename from media/java/android/media/session/RemoteVolumeProvider.java
rename to media/java/android/media/VolumeProvider.java
index 606b1d7..7d93b40 100644
--- a/media/java/android/media/session/RemoteVolumeProvider.java
+++ b/media/java/android/media/VolumeProvider.java
@@ -13,8 +13,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package android.media.session;
+package android.media;
 
+import android.media.session.MediaSession;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -24,8 +25,8 @@
  * You can set a volume provider on a session by calling
  * {@link MediaSession#setPlaybackToRemote}.
  */
-public abstract class RemoteVolumeProvider {
-    private static final String TAG = "RemoteVolumeProvider";
+public abstract class VolumeProvider {
+    private static final String TAG = "VolumeProvider";
 
     /**
      * The volume is fixed and can not be modified. Requests to change volume
@@ -60,7 +61,7 @@
      *            this provider.
      * @param maxVolume The maximum allowed volume.
      */
-    public RemoteVolumeProvider(int volumeControl, int maxVolume) {
+    public VolumeProvider(int volumeControl, int maxVolume) {
         mControlType = volumeControl;
         mMaxVolume = maxVolume;
     }
@@ -117,7 +118,7 @@
     /**
      * @hide
      */
-    void setSession(MediaSession session) {
+    public void setSession(MediaSession session) {
         mSession = session;
     }
 }
\ No newline at end of file
diff --git a/media/java/android/media/session/ISession.aidl b/media/java/android/media/session/ISession.aidl
index 1cfc5bc..5bc0de4 100644
--- a/media/java/android/media/session/ISession.aidl
+++ b/media/java/android/media/session/ISession.aidl
@@ -15,6 +15,7 @@
 
 package android.media.session;
 
+import android.content.ComponentName;
 import android.media.MediaMetadata;
 import android.media.session.ISessionController;
 import android.media.session.RouteOptions;
@@ -33,6 +34,7 @@
     ISessionController getController();
     void setFlags(int flags);
     void setActive(boolean active);
+    void setMediaButtonReceiver(in ComponentName mbr);
     void destroy();
 
     // These commands are for setting up and communicating with routes
diff --git a/media/java/android/media/session/ISessionController.aidl b/media/java/android/media/session/ISessionController.aidl
index f0cd785..b4c11f6 100644
--- a/media/java/android/media/session/ISessionController.aidl
+++ b/media/java/android/media/session/ISessionController.aidl
@@ -20,6 +20,7 @@
 import android.media.Rating;
 import android.media.session.ISessionControllerCallback;
 import android.media.session.MediaSessionInfo;
+import android.media.session.ParcelableVolumeInfo;
 import android.media.session.PlaybackState;
 import android.os.Bundle;
 import android.os.ResultReceiver;
@@ -38,6 +39,9 @@
     void showRoutePicker();
     MediaSessionInfo getSessionInfo();
     long getFlags();
+    ParcelableVolumeInfo getVolumeAttributes();
+    void adjustVolumeBy(int delta, int flags);
+    void setVolumeTo(int value, int flags);
 
     // These commands are for the TransportController
     void play();
diff --git a/media/java/android/media/session/MediaController.java b/media/java/android/media/session/MediaController.java
index 5ca7daa..84dad25 100644
--- a/media/java/android/media/session/MediaController.java
+++ b/media/java/android/media/session/MediaController.java
@@ -18,6 +18,7 @@
 
 import android.media.MediaMetadata;
 import android.media.Rating;
+import android.media.VolumeProvider;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -70,14 +71,7 @@
      * @hide
      */
     public static MediaController fromBinder(ISessionController sessionBinder) {
-        MediaController controller = new MediaController(sessionBinder);
-        try {
-            controller.mSessionBinder.registerCallbackListener(controller.mCbStub);
-        } catch (RemoteException e) {
-            Log.wtf(TAG, "MediaController created with expired token", e);
-            controller = null;
-        }
-        return controller;
+        return new MediaController(sessionBinder);
     }
 
     /**
@@ -190,6 +184,23 @@
     }
 
     /**
+     * Get the current volume info for this session.
+     *
+     * @return The current volume info or null.
+     */
+    public VolumeInfo getVolumeInfo() {
+        try {
+            ParcelableVolumeInfo result = mSessionBinder.getVolumeAttributes();
+            return new VolumeInfo(result.volumeType, result.audioStream, result.controlType,
+                    result.maxVolume, result.currentVolume);
+
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Error calling getVolumeInfo.", e);
+        }
+        return null;
+    }
+
+    /**
      * Adds a callback to receive updates from the Session. Updates will be
      * posted on the caller's thread.
      *
@@ -305,7 +316,7 @@
                 mSessionBinder.registerCallbackListener(mCbStub);
                 mCbRegistered = true;
             } catch (RemoteException e) {
-                Log.d(TAG, "Dead object in registerCallback", e);
+                Log.e(TAG, "Dead object in registerCallback", e);
             }
         }
     }
@@ -314,14 +325,23 @@
         if (cb == null) {
             throw new IllegalArgumentException("Callback cannot be null");
         }
+        boolean success = false;
         for (int i = mCallbacks.size() - 1; i >= 0; i--) {
             MessageHandler handler = mCallbacks.get(i);
             if (cb == handler.mCallback) {
                 mCallbacks.remove(i);
-                return true;
+                success = true;
             }
         }
-        return false;
+        if (mCbRegistered && mCallbacks.size() == 0) {
+            try {
+                mSessionBinder.unregisterCallbackListener(mCbStub);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Dead object in removeCallbackLocked");
+            }
+            mCbRegistered = false;
+        }
+        return success;
     }
 
     private MessageHandler getHandlerForCallbackLocked(Callback cb) {
@@ -507,6 +527,85 @@
         }
     }
 
+    /**
+     * Holds information about the way volume is handled for this session.
+     */
+    public static final class VolumeInfo {
+        private final int mVolumeType;
+        private final int mAudioStream;
+        private final int mVolumeControl;
+        private final int mMaxVolume;
+        private final int mCurrentVolume;
+
+        /**
+         * @hide
+         */
+        public VolumeInfo(int type, int stream, int control, int max, int current) {
+            mVolumeType = type;
+            mAudioStream = stream;
+            mVolumeControl = control;
+            mMaxVolume = max;
+            mCurrentVolume = current;
+        }
+
+        /**
+         * Get the type of volume handling, either local or remote. One of:
+         * <ul>
+         * <li>{@link MediaSession#VOLUME_TYPE_LOCAL}</li>
+         * <li>{@link MediaSession#VOLUME_TYPE_REMOTE}</li>
+         * </ul>
+         *
+         * @return The type of volume handling this session is using.
+         */
+        public int getVolumeType() {
+            return mVolumeType;
+        }
+
+        /**
+         * Get the stream this is currently controlling volume on. When the volume
+         * type is {@link MediaSession#VOLUME_TYPE_REMOTE} this value does not
+         * have meaning and should be ignored.
+         *
+         * @return The stream this session is playing on.
+         */
+        public int getAudioStream() {
+            return mAudioStream;
+        }
+
+        /**
+         * Get the type of volume control that can be used. One of:
+         * <ul>
+         * <li>{@link VolumeProvider#VOLUME_CONTROL_ABSOLUTE}</li>
+         * <li>{@link VolumeProvider#VOLUME_CONTROL_RELATIVE}</li>
+         * <li>{@link VolumeProvider#VOLUME_CONTROL_FIXED}</li>
+         * </ul>
+         *
+         * @return The type of volume control that may be used with this
+         *         session.
+         */
+        public int getVolumeControl() {
+            return mVolumeControl;
+        }
+
+        /**
+         * Get the maximum volume that may be set for this session.
+         *
+         * @return The maximum allowed volume where this session is playing.
+         */
+        public int getMaxVolume() {
+            return mMaxVolume;
+        }
+
+        /**
+         * Get the current volume for this session.
+         *
+         * @return The current volume where this session is playing.
+         */
+        public int getCurrentVolume() {
+            return mCurrentVolume;
+        }
+    }
+
     private final static class CallbackStub extends ISessionControllerCallback.Stub {
         private final WeakReference<MediaController> mController;
 
diff --git a/media/java/android/media/session/MediaSession.java b/media/java/android/media/session/MediaSession.java
index 4ba1351..406b1c3 100644
--- a/media/java/android/media/session/MediaSession.java
+++ b/media/java/android/media/session/MediaSession.java
@@ -19,10 +19,12 @@
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.PendingIntent;
+import android.content.ComponentName;
 import android.content.Intent;
 import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.Rating;
+import android.media.VolumeProvider;
 import android.media.session.ISessionController;
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
@@ -125,18 +127,12 @@
     public static final int DISCONNECT_REASON_SESSION_DESTROYED = 5;
 
     /**
-     * The session uses local playback. Used for configuring volume handling
-     * with the system.
-     *
-     * @hide
+     * The session uses local playback.
      */
     public static final int VOLUME_TYPE_LOCAL = 1;
 
     /**
-     * The session uses remote playback. Used for configuring volume handling
-     * with the system.
-     *
-     * @hide
+     * The session uses remote playback.
      */
     public static final int VOLUME_TYPE_REMOTE = 2;
 
@@ -155,7 +151,7 @@
             = new ArrayMap<String, RouteInterface.EventListener>();
 
     private Route mRoute;
-    private RemoteVolumeProvider mVolumeProvider;
+    private VolumeProvider mVolumeProvider;
 
     private boolean mActive = false;;
 
@@ -232,6 +228,21 @@
     }
 
     /**
+     * Set a media button event receiver component to use to restart playback
+     * after an app has been stopped.
+     *
+     * @param mbr The receiver component to send the media button event to.
+     * @hide
+     */
+    public void setMediaButtonReceiver(ComponentName mbr) {
+        try {
+            mBinder.setMediaButtonReceiver(mbr);
+        } catch (RemoteException e) {
+            Log.wtf(TAG, "Failure in setMediaButtonReceiver.", e);
+        }
+    }
+
+    /**
      * Set any flags for the session.
      *
      * @param flags The flags to set for this session.
@@ -272,7 +283,7 @@
      * @param volumeProvider The provider that will handle volume changes. May
      *            not be null.
      */
-    public void setPlaybackToRemote(RemoteVolumeProvider volumeProvider) {
+    public void setPlaybackToRemote(VolumeProvider volumeProvider) {
         if (volumeProvider == null) {
             throw new IllegalArgumentException("volumeProvider may not be null!");
         }
@@ -524,12 +535,12 @@
     }
 
     /**
-     * Notify the system that the remove volume changed.
+     * Notify the system that the remote volume changed.
      *
      * @param provider The provider that is handling volume changes.
      * @hide
      */
-    void notifyRemoteVolumeChanged(RemoteVolumeProvider provider) {
+    public void notifyRemoteVolumeChanged(VolumeProvider provider) {
         if (provider == null || provider != mVolumeProvider) {
             Log.w(TAG, "Received update from stale volume provider");
             return;
diff --git a/media/java/android/media/session/MediaSessionLegacyHelper.java b/media/java/android/media/session/MediaSessionLegacyHelper.java
index 801844f..838b857 100644
--- a/media/java/android/media/session/MediaSessionLegacyHelper.java
+++ b/media/java/android/media/session/MediaSessionLegacyHelper.java
@@ -18,6 +18,7 @@
 
 import android.app.PendingIntent;
 import android.app.PendingIntent.CanceledException;
+import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.media.MediaMetadata;
@@ -214,7 +215,7 @@
         }
     }
 
-    public void addMediaButtonListener(PendingIntent pi,
+    public void addMediaButtonListener(PendingIntent pi, ComponentName mbrComponent,
             Context context) {
         if (pi == null) {
             Log.w(TAG, "Pending intent was null, can't addMediaButtonListener.");
@@ -238,6 +239,7 @@
 
         holder.mMediaButtonReceiver = new MediaButtonReceiver(pi, context);
         holder.mSession.addCallback(holder.mMediaButtonReceiver, mHandler);
+        holder.mSession.setMediaButtonReceiver(mbrComponent);
         if (DEBUG) {
             Log.d(TAG, "addMediaButtonListener added " + pi);
         }
diff --git a/media/java/android/media/session/MediaSessionManager.java b/media/java/android/media/session/MediaSessionManager.java
index 2e6b86e..1ff49d8 100644
--- a/media/java/android/media/session/MediaSessionManager.java
+++ b/media/java/android/media/session/MediaSessionManager.java
@@ -109,7 +109,6 @@
      * @param notificationListener The enabled notification listener component.
      *            May be null.
      * @return A list of controllers for ongoing sessions
-     * @hide
      */
     public List<MediaController> getActiveSessions(ComponentName notificationListener) {
         return getActiveSessionsForUser(notificationListener, UserHandle.myUserId());
@@ -157,6 +156,24 @@
      * @param sessionListener The listener to add.
      * @param notificationListener The enabled notification listener component.
      *            May be null.
+     */
+    public void addActiveSessionsListener(SessionListener sessionListener,
+            ComponentName notificationListener) {
+        addActiveSessionsListener(sessionListener, notificationListener, UserHandle.myUserId());
+    }
+
+    /**
+     * Add a listener to be notified when the list of active sessions
+     * changes.This requires the
+     * android.Manifest.permission.MEDIA_CONTENT_CONTROL permission be held by
+     * the calling app. You may also retrieve this list if your app is an
+     * enabled notification listener using the
+     * {@link NotificationListenerService} APIs, in which case you must pass the
+     * {@link ComponentName} of your enabled listener.
+     *
+     * @param sessionListener The listener to add.
+     * @param notificationListener The enabled notification listener component.
+     *            May be null.
      * @param userId The userId to listen for changes on.
      * @hide
      */
@@ -236,8 +253,6 @@
     /**
      * Listens for changes to the list of active sessions. This can be added
      * using {@link #addActiveSessionsListener}.
-     *
-     * @hide
      */
     public static abstract class SessionListener {
         /**
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.aidl b/media/java/android/media/session/ParcelableVolumeInfo.aidl
new file mode 100644
index 0000000..c4250f0
--- /dev/null
+++ b/media/java/android/media/session/ParcelableVolumeInfo.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2014, 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.session;
+
+parcelable ParcelableVolumeInfo;
diff --git a/media/java/android/media/session/ParcelableVolumeInfo.java b/media/java/android/media/session/ParcelableVolumeInfo.java
new file mode 100644
index 0000000..166ccd3
--- /dev/null
+++ b/media/java/android/media/session/ParcelableVolumeInfo.java
@@ -0,0 +1,78 @@
+/* Copyright 2014, 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.session;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Convenience class for passing information about the audio configuration of a
+ * session. The public implementation is {@link MediaController.VolumeInfo}.
+ *
+ * @hide
+ */
+public class ParcelableVolumeInfo implements Parcelable {
+    public int volumeType;
+    public int audioStream;
+    public int controlType;
+    public int maxVolume;
+    public int currentVolume;
+
+    public ParcelableVolumeInfo(int volumeType, int audioStream, int controlType, int maxVolume,
+            int currentVolume) {
+        this.volumeType = volumeType;
+        this.audioStream = audioStream;
+        this.controlType = controlType;
+        this.maxVolume = maxVolume;
+        this.currentVolume = currentVolume;
+    }
+
+    public ParcelableVolumeInfo(Parcel from) {
+        volumeType = from.readInt();
+        audioStream = from.readInt();
+        controlType = from.readInt();
+        maxVolume = from.readInt();
+        currentVolume = from.readInt();
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(volumeType);
+        dest.writeInt(audioStream);
+        dest.writeInt(controlType);
+        dest.writeInt(maxVolume);
+        dest.writeInt(currentVolume);
+    }
+
+
+    public static final Parcelable.Creator<ParcelableVolumeInfo> CREATOR
+            = new Parcelable.Creator<ParcelableVolumeInfo>() {
+        @Override
+        public ParcelableVolumeInfo createFromParcel(Parcel in) {
+            return new ParcelableVolumeInfo(in);
+        }
+
+        @Override
+        public ParcelableVolumeInfo[] newArray(int size) {
+            return new ParcelableVolumeInfo[size];
+        }
+    };
+}
diff --git a/media/java/android/media/tv/TvContract.java b/media/java/android/media/tv/TvContract.java
index 3f0405d..7e9d279 100644
--- a/media/java/android/media/tv/TvContract.java
+++ b/media/java/android/media/tv/TvContract.java
@@ -22,7 +22,9 @@
 import android.net.Uri;
 import android.provider.BaseColumns;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 
 /**
  * <p>
@@ -380,6 +382,81 @@
         /** The service type for radio channels that have audio only. */
         public static final int SERVICE_TYPE_AUDIO = 0x2;
 
+        /** The video format for 240p. */
+        public static final String VIDEO_FORMAT_240P = "VIDEO_FORMAT_240P";
+
+        /** The video format for 360p. */
+        public static final String VIDEO_FORMAT_360P = "VIDEO_FORMAT_360P";
+
+        /** The video format for 480i. */
+        public static final String VIDEO_FORMAT_480I = "VIDEO_FORMAT_480I";
+
+        /** The video format for 480p. */
+        public static final String VIDEO_FORMAT_480P = "VIDEO_FORMAT_480P";
+
+        /** The video format for 576i. */
+        public static final String VIDEO_FORMAT_576I = "VIDEO_FORMAT_576I";
+
+        /** The video format for 576p. */
+        public static final String VIDEO_FORMAT_576P = "VIDEO_FORMAT_576P";
+
+        /** The video format for 720p. */
+        public static final String VIDEO_FORMAT_720P = "VIDEO_FORMAT_720P";
+
+        /** The video format for 1080i. */
+        public static final String VIDEO_FORMAT_1080I = "VIDEO_FORMAT_1080I";
+
+        /** The video format for 1080p. */
+        public static final String VIDEO_FORMAT_1080P = "VIDEO_FORMAT_1080P";
+
+        /** The video format for 2160p. */
+        public static final String VIDEO_FORMAT_2160P = "VIDEO_FORMAT_2160P";
+
+        /** The video format for 4320p. */
+        public static final String VIDEO_FORMAT_4320P = "VIDEO_FORMAT_4320P";
+
+        /** The video resolution for standard-definition. */
+        public static final String VIDEO_RESOLUTION_SD = "VIDEO_RESOLUTION_SD";
+
+        /** The video resolution for enhanced-definition. */
+        public static final String VIDEO_RESOLUTION_ED = "VIDEO_RESOLUTION_ED";
+
+        /** The video resolution for high-definition. */
+        public static final String VIDEO_RESOLUTION_HD = "VIDEO_RESOLUTION_HD";
+
+        /** The video resolution for full high-definition. */
+        public static final String VIDEO_RESOLUTION_FHD = "VIDEO_RESOLUTION_FHD";
+
+        /** The video resolution for ultra high-definition. */
+        public static final String VIDEO_RESOLUTION_UHD = "VIDEO_RESOLUTION_UHD";
+
+        private static final Map<String, String> VIDEO_FORMAT_TO_RESOLUTION_MAP =
+                new HashMap<String, String>();
+
+        static {
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_480I, VIDEO_RESOLUTION_SD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_480P, VIDEO_RESOLUTION_ED);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_576I, VIDEO_RESOLUTION_SD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_576P, VIDEO_RESOLUTION_ED);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_720P, VIDEO_RESOLUTION_HD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_1080I, VIDEO_RESOLUTION_HD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_1080P, VIDEO_RESOLUTION_FHD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_2160P, VIDEO_RESOLUTION_UHD);
+            VIDEO_FORMAT_TO_RESOLUTION_MAP.put(VIDEO_FORMAT_4320P, VIDEO_RESOLUTION_UHD);
+        }
+
+        /**
+         * Returns the video resolution (definition) for a given video format.
+         *
+         * @param videoFormat The video format defined in {@link Channels}.
+         * @return the corresponding video resolution string. {@code null} if the resolution string
+         *         is not defined for the given video format.
+         * @see #COLUMN_VIDEO_FORMAT
+         */
+        public static final String getVideoResolution(String videoFormat) {
+            return VIDEO_FORMAT_TO_RESOLUTION_MAP.get(videoFormat);
+        }
+
         /**
          * The name of the {@link TvInputService} subclass that provides this TV channel. This
          * should be a fully qualified class name (such as, "com.example.project.TvInputService").
@@ -513,6 +590,24 @@
         public static final String COLUMN_DESCRIPTION = "description";
 
         /**
+         * The typical video format for programs from this TV channel.
+         * <p>
+         * This is primarily used to filter out channels based on video format by applications. The
+         * value should match one of the followings: {@link #VIDEO_FORMAT_240P},
+         * {@link #VIDEO_FORMAT_360P}, {@link #VIDEO_FORMAT_480I}, {@link #VIDEO_FORMAT_480P},
+         * {@link #VIDEO_FORMAT_576I}, {@link #VIDEO_FORMAT_576P}, {@link #VIDEO_FORMAT_720P},
+         * {@link #VIDEO_FORMAT_1080I}, {@link #VIDEO_FORMAT_1080P}, {@link #VIDEO_FORMAT_2160P},
+         * {@link #VIDEO_FORMAT_4320P}. Note that the actual video resolution of each program from a
+         * given channel can vary thus one should use {@link Programs#COLUMN_VIDEO_WIDTH} and
+         * {@link Programs#COLUMN_VIDEO_HEIGHT} to get more accurate video resolution.
+         * </p><p>
+         * Type: TEXT
+         * </p><p>
+         * @see #getVideoResolution
+         */
+        public static final String COLUMN_VIDEO_FORMAT = "video_format";
+
+        /**
          * The flag indicating whether this TV channel is browsable or not.
          * <p>
          * A value of 1 indicates the channel is included in the channel list that applications use
@@ -719,6 +814,32 @@
         public static final String COLUMN_LONG_DESCRIPTION = "long_description";
 
         /**
+         * The width of the video for this TV program, in the unit of pixels.
+         * <p>
+         * Together with {@link #COLUMN_VIDEO_HEIGHT} this is used to determine the video resolution
+         * of the current TV program. Can be empty if it is not known initially or the program does
+         * not convey any video such as the programs from type {@link Channels#SERVICE_TYPE_AUDIO}
+         * channels.
+         * </p><p>
+         * Type: INTEGER
+         * </p>
+         */
+        public static final String COLUMN_VIDEO_WIDTH = "video_width";
+
+        /**
+         * The height of the video for this TV program, in the unit of pixels.
+         * <p>
+         * Together with {@link #COLUMN_VIDEO_WIDTH} this is used to determine the video resolution
+         * of the current TV program. Can be empty if it is not known initially or the program does
+         * not convey any video such as the programs from type {@link Channels#SERVICE_TYPE_AUDIO}
+         * channels.
+         * </p><p>
+         * Type: INTEGER
+         * </p>
+         */
+        public static final String COLUMN_VIDEO_HEIGHT = "video_height";
+
+        /**
          * The comma-separated audio languages of this TV program.
          * <p>
          * This is used to describe available audio languages included in the program. Use
@@ -778,37 +899,37 @@
         /** Canonical genres for TV programs. */
         public static final class Genres {
             /** The genre for Family/Kids. */
-            public static final String FAMILY_KIDS = "Family/Kids";
+            public static final String FAMILY_KIDS = "FAMILY_KIDS";
 
             /** The genre for Sports. */
-            public static final String SPORTS = "Sports";
+            public static final String SPORTS = "SPORTS";
 
             /** The genre for Shopping. */
-            public static final String SHOPPING = "Shopping";
+            public static final String SHOPPING = "SHOPPING";
 
             /** The genre for Movies. */
-            public static final String MOVIES = "Movies";
+            public static final String MOVIES = "MOVIES";
 
             /** The genre for Comedy. */
-            public static final String COMEDY = "Comedy";
+            public static final String COMEDY = "COMEDY";
 
             /** The genre for Travel. */
-            public static final String TRAVEL = "Travel";
+            public static final String TRAVEL = "TRAVEL";
 
             /** The genre for Drama. */
-            public static final String DRAMA = "Drama";
+            public static final String DRAMA = "DRAMA";
 
             /** The genre for Education. */
-            public static final String EDUCATION = "Education";
+            public static final String EDUCATION = "EDUCATION";
 
             /** The genre for Animal/Wildlife. */
-            public static final String ANIMAL_WILDLIFE = "Animal/Wildlife";
+            public static final String ANIMAL_WILDLIFE = "ANIMAL_WILDLIFE";
 
             /** The genre for News. */
-            public static final String NEWS = "News";
+            public static final String NEWS = "NEWS";
 
             /** The genre for Gaming. */
-            public static final String GAMING = "Gaming";
+            public static final String GAMING = "GAMING";
 
             private Genres() {}
 
@@ -848,7 +969,7 @@
      *
      * @hide
      */
-    public static final class WatchedPrograms implements BaseColumns {
+    public static final class WatchedPrograms implements BaseTvColumns {
 
         /** The content:// style URI for this table. */
         public static final Uri CONTENT_URI =
diff --git a/media/java/android/media/tv/TvInputHardwareInfo.java b/media/java/android/media/tv/TvInputHardwareInfo.java
index 4beb960..e5f9889 100644
--- a/media/java/android/media/tv/TvInputHardwareInfo.java
+++ b/media/java/android/media/tv/TvInputHardwareInfo.java
@@ -16,6 +16,7 @@
 
 package android.media.tv;
 
+import android.media.AudioManager;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.Log;
@@ -56,14 +57,11 @@
 
     private int mDeviceId;
     private int mType;
-    // TODO: Add audio port & audio address for audio service.
-    // TODO: Add HDMI handle for HDMI service.
+    private int mAudioType;
+    private String mAudioAddress;
+    private int mHdmiPortId;
 
-    public TvInputHardwareInfo() { }
-
-    public TvInputHardwareInfo(int deviceId, int type) {
-        mDeviceId = deviceId;
-        mType = type;
+    private TvInputHardwareInfo() {
     }
 
     public int getDeviceId() {
@@ -74,6 +72,21 @@
         return mType;
     }
 
+    public int getAudioType() {
+        return mAudioType;
+    }
+
+    public String getAudioAddress() {
+        return mAudioAddress;
+    }
+
+    public int getHdmiPortId() {
+        if (mType != TV_INPUT_TYPE_HDMI) {
+            throw new IllegalStateException();
+        }
+        return mHdmiPortId;
+    }
+
     // Parcelable
     @Override
     public int describeContents() {
@@ -84,10 +97,78 @@
     public void writeToParcel(Parcel dest, int flags) {
         dest.writeInt(mDeviceId);
         dest.writeInt(mType);
+        dest.writeInt(mAudioType);
+        dest.writeString(mAudioAddress);
+        if (mType == TV_INPUT_TYPE_HDMI) {
+            dest.writeInt(mHdmiPortId);
+        }
     }
 
     public void readFromParcel(Parcel source) {
         mDeviceId = source.readInt();
         mType = source.readInt();
+        mAudioType = source.readInt();
+        mAudioAddress = source.readString();
+        if (mType == TV_INPUT_TYPE_HDMI) {
+            mHdmiPortId = source.readInt();
+        }
+    }
+
+    public static final class Builder {
+        private Integer mDeviceId = null;
+        private Integer mType = null;
+        private int mAudioType = AudioManager.DEVICE_NONE;
+        private String mAudioAddress = "";
+        private Integer mHdmiPortId = null;
+
+        public Builder() {
+        }
+
+        public Builder deviceId(int deviceId) {
+            mDeviceId = deviceId;
+            return this;
+        }
+
+        public Builder type(int type) {
+            mType = type;
+            return this;
+        }
+
+        public Builder audioType(int audioType) {
+            mAudioType = audioType;
+            return this;
+        }
+
+        public Builder audioAddress(String audioAddress) {
+            mAudioAddress = audioAddress;
+            return this;
+        }
+
+        public Builder hdmiPortId(int hdmiPortId) {
+            mHdmiPortId = hdmiPortId;
+            return this;
+        }
+
+        public TvInputHardwareInfo build() {
+            if (mDeviceId == null || mType == null) {
+                throw new UnsupportedOperationException();
+            }
+            if ((mType == TV_INPUT_TYPE_HDMI && mHdmiPortId == null) ||
+                    (mType != TV_INPUT_TYPE_HDMI && mHdmiPortId != null)) {
+                throw new UnsupportedOperationException();
+            }
+
+            TvInputHardwareInfo info = new TvInputHardwareInfo();
+            info.mDeviceId = mDeviceId;
+            info.mType = mType;
+            info.mAudioType = mAudioType;
+            if (info.mAudioType != AudioManager.DEVICE_NONE) {
+                info.mAudioAddress = mAudioAddress;
+            }
+            if (mHdmiPortId != null) {
+                info.mHdmiPortId = mHdmiPortId;
+            }
+            return info;
+        }
     }
 }
diff --git a/media/java/android/media/tv/TvInputManager.java b/media/java/android/media/tv/TvInputManager.java
index edfdd60..daa7009 100644
--- a/media/java/android/media/tv/TvInputManager.java
+++ b/media/java/android/media/tv/TvInputManager.java
@@ -533,12 +533,11 @@
 
         /**
          * Releases this session.
-         *
-         * @throws IllegalStateException if the session has been already released.
          */
         public void release() {
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 mService.releaseSession(mToken, mUserId);
@@ -553,12 +552,12 @@
          * Sets the {@link android.view.Surface} for this session.
          *
          * @param surface A {@link android.view.Surface} used to render video.
-         * @throws IllegalStateException if the session has been already released.
          * @hide
          */
         public void setSurface(Surface surface) {
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             // surface can be null.
             try {
@@ -573,11 +572,11 @@
          *
          * @param volume A volume value between 0.0f to 1.0f.
          * @throws IllegalArgumentException if the volume value is out of range.
-         * @throws IllegalStateException if the session has been already released.
          */
         public void setStreamVolume(float volume) {
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 if (volume < 0.0f || volume > 1.0f) {
@@ -594,14 +593,14 @@
          *
          * @param channelUri The URI of a channel.
          * @throws IllegalArgumentException if the argument is {@code null}.
-         * @throws IllegalStateException if the session has been already released.
          */
         public void tune(Uri channelUri) {
             if (channelUri == null) {
                 throw new IllegalArgumentException("channelUri cannot be null");
             }
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 mService.tune(mToken, channelUri, mUserId);
@@ -620,8 +619,7 @@
          * @param view A view playing TV.
          * @param frame A position of the overlay view.
          * @throws IllegalArgumentException if any of the arguments is {@code null}.
-         * @throws IllegalStateException if {@code view} is not attached to a window or
-         *         if the session has been already released.
+         * @throws IllegalStateException if {@code view} is not attached to a window.
          */
         void createOverlayView(View view, Rect frame) {
             if (view == null) {
@@ -634,7 +632,8 @@
                 throw new IllegalStateException("view must be attached to a window");
             }
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 mService.createOverlayView(mToken, view.getWindowToken(), frame, mUserId);
@@ -648,14 +647,14 @@
          *
          * @param frame A new position of the overlay view.
          * @throws IllegalArgumentException if the arguments is {@code null}.
-         * @throws IllegalStateException if the session has been already released.
          */
         void relayoutOverlayView(Rect frame) {
             if (frame == null) {
                 throw new IllegalArgumentException("frame cannot be null");
             }
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 mService.relayoutOverlayView(mToken, frame, mUserId);
@@ -666,12 +665,11 @@
 
         /**
          * Removes the current overlay view.
-         *
-         * @throws IllegalStateException if the session has been already released.
          */
         void removeOverlayView() {
             if (mToken == null) {
-                throw new IllegalStateException("the session has been already released");
+                Log.w(TAG, "The session has been already released");
+                return;
             }
             try {
                 mService.removeOverlayView(mToken, mUserId);
diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp
index 36cfb0f..ad7ee7a 100644
--- a/media/jni/android_media_ImageReader.cpp
+++ b/media/jni/android_media_ImageReader.cpp
@@ -33,6 +33,9 @@
 #include <jni.h>
 #include <JNIHelp.h>
 
+#include <stdint.h>
+#include <inttypes.h>
+
 #define ALIGN(x, mask) ( ((x) + (mask) - 1) & ~((mask) - 1) )
 
 #define ANDROID_MEDIA_IMAGEREADER_CTX_JNI_ID       "mNativeContext"
@@ -300,6 +303,14 @@
 
     // failed to find size, default to whole buffer
     if (size == 0) {
+        /*
+         * This is a problem because not including the JPEG header
+         * means that in certain rare situations a regular JPEG blob
+         * will be misidentified as having a header, in which case
+         * we will get a garbage size value.
+         */
+        ALOGW("%s: No JPEG header detected, defaulting to size=width=%d",
+                __FUNCTION__, width);
         size = width;
     }
 
@@ -767,11 +778,12 @@
     int imgReaderFmt = ctx->getBufferFormat();
     int bufFmt = buffer->format;
     if (imgReaderFmt != bufFmt) {
-        // Special casing for when producer switches format
-        if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && bufFmt ==
-                HAL_PIXEL_FORMAT_YCrCb_420_SP) {
-            ctx->setBufferFormat(HAL_PIXEL_FORMAT_YCrCb_420_SP);
-            ALOGV("%s: Overriding NV21 to YUV_420_888.", __FUNCTION__);
+        // Special casing for when producer switches to a format compatible with flexible YUV
+        // (HAL_PIXEL_FORMAT_YCbCr_420_888).
+        if (imgReaderFmt == HAL_PIXEL_FORMAT_YCbCr_420_888 && (bufFmt ==
+                HAL_PIXEL_FORMAT_YCrCb_420_SP || bufFmt == HAL_PIXEL_FORMAT_YV12)) {
+            ctx->setBufferFormat(bufFmt);
+            ALOGV("%s: Overriding buffer format YUV_420_888 to %x.", __FUNCTION__, bufFmt);
         } else {
             // Return the buffer to the queue.
             consumer->unlockBuffer(*buffer);
@@ -847,6 +859,14 @@
 
     // Create byteBuffer from native buffer
     Image_getLockedBufferInfo(env, buffer, idx, &base, &size);
+
+    if (size > static_cast<uint32_t>(INT32_MAX)) {
+        // Byte buffer have 'int capacity', so check the range
+        jniThrowExceptionFmt(env, "java/lang/IllegalStateException",
+                "Size too large for bytebuffer capacity " PRIu32, size);
+        return NULL;
+    }
+
     byteBuffer = env->NewDirectByteBuffer(base, size);
     // TODO: throw dvm exOutOfMemoryError?
     if ((byteBuffer == NULL) && (env->ExceptionCheck() == false)) {
diff --git a/media/jni/android_media_MediaDrm.cpp b/media/jni/android_media_MediaDrm.cpp
index 4fbd2a4..5f27b16 100644
--- a/media/jni/android_media_MediaDrm.cpp
+++ b/media/jni/android_media_MediaDrm.cpp
@@ -110,6 +110,11 @@
     jfieldID certificateData;
 };
 
+struct StateExceptionFields {
+    jmethodID init;
+    jclass classId;
+};
+
 struct fields_t {
     jfieldID context;
     jmethodID post_event;
@@ -121,6 +126,7 @@
     IteratorFields iterator;
     EntryFields entry;
     CertificateFields certificate;
+    StateExceptionFields stateException;
     jclass certificateClassId;
     jclass hashmapClassId;
     jclass arraylistClassId;
@@ -212,6 +218,14 @@
     }
 }
 
+static void throwStateException(JNIEnv *env, const char *msg, status_t err) {
+    ALOGE("Illegal state exception: %s (%d)", msg, err);
+
+    jobject exception = env->NewObject(gFields.stateException.classId,
+            gFields.stateException.init, static_cast<int>(err),
+            env->NewStringUTF(msg));
+    env->Throw(static_cast<jthrowable>(exception));
+}
 
 static bool throwExceptionAsNecessary(
         JNIEnv *env, status_t err, const char *msg = NULL) {
@@ -275,8 +289,7 @@
                 msg = errbuf.string();
             }
         }
-        ALOGE("Illegal state exception: %s", msg);
-        jniThrowException(env, "java/lang/IllegalStateException", msg);
+        throwStateException(env, msg, err);
         return true;
     }
     return false;
@@ -608,6 +621,10 @@
 
     FIND_CLASS(clazz, "java/util/ArrayList");
     gFields.arraylistClassId = static_cast<jclass>(env->NewGlobalRef(clazz));
+
+    FIND_CLASS(clazz, "android/media/MediaDrm$MediaDrmStateException");
+    GET_METHOD_ID(gFields.stateException.init, clazz, "<init>", "(ILjava/lang/String;)V");
+    gFields.stateException.classId = static_cast<jclass>(env->NewGlobalRef(clazz));
 }
 
 static void android_media_MediaDrm_native_setup(
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
index 8a7e642..b6bb578 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTest.java
@@ -22,7 +22,6 @@
 import android.hardware.ICameraServiceListener;
 import android.hardware.IProCameraCallbacks;
 import android.hardware.IProCameraUser;
-import android.hardware.camera2.CameraMetadata;
 import android.hardware.camera2.ICameraDeviceCallbacks;
 import android.hardware.camera2.ICameraDeviceUser;
 import android.hardware.camera2.impl.CameraMetadataNative;
@@ -51,8 +50,14 @@
  * </pre>
  */
 public class CameraBinderTest extends AndroidTestCase {
+    private static final int MAX_PARAMETERS_LENGTH = 100;
+
     static String TAG = "CameraBinderTest";
 
+    // From ICameraService.h
+    private static final int API_VERSION_1 = 1;
+    private static final int API_VERSION_2 = 2;
+
     protected CameraBinderTestUtils mUtils;
 
     public CameraBinderTest() {
@@ -95,6 +100,56 @@
         }
     }
 
+    @SmallTest
+    public void testGetLegacyParameters() throws Exception {
+        for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+            String[] parameters = new String[1];
+            assertEquals("Camera service returned parameters for camera " + cameraId,
+                    CameraBinderTestUtils.NO_ERROR,
+                    mUtils.getCameraService().getLegacyParameters(cameraId, /*out*/parameters));
+            assertNotNull(parameters[0]);
+            assertTrue("Parameters should have at least one character in it",
+                    parameters[0].length() > 0);
+
+            int end = parameters[0].length();
+            if (end > MAX_PARAMETERS_LENGTH) {
+                end = MAX_PARAMETERS_LENGTH;
+            }
+
+            Log.v(TAG, "Camera " + cameraId + " parameters: " + parameters[0].substring(0, end));
+        }
+    }
+
+    /** The camera2 api is only supported on HAL3.2+ devices */
+    @SmallTest
+    public void testSupportsCamera2Api() throws Exception {
+        for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+            int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_2);
+
+            if (res != CameraBinderTestUtils.NO_ERROR && res != CameraBinderTestUtils.EOPNOTSUPP) {
+                fail("Camera service returned bad value when queried if it supports camera2 api: "
+                        + res + " for camera ID " + cameraId);
+            }
+
+            boolean supports = res == CameraBinderTestUtils.NO_ERROR;
+            Log.v(TAG, "Camera " + cameraId + " supports api2: " + supports);
+        }
+    }
+
+    /** The camera1 api is supported on *all* devices regardless of HAL version */
+    @SmallTest
+    public void testSupportsCamera1Api() throws Exception {
+        for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+
+            int res = mUtils.getCameraService().supportsCameraApi(cameraId, API_VERSION_1);
+            assertEquals(
+                    "Camera service returned bad value when queried if it supports camera1 api: "
+                    + res + " for camera ID " + cameraId, CameraBinderTestUtils.NO_ERROR, res);
+        }
+    }
+
     static abstract class DummyBase extends Binder implements android.os.IInterface {
         @Override
         public IBinder asBinder() {
@@ -150,6 +205,37 @@
         }
     }
 
+    @SmallTest
+    public void testConnectLegacy() throws Exception {
+        final int CAMERA_HAL_API_VERSION_1_0 = 0x100;
+        for (int cameraId = 0; cameraId < mUtils.getGuessedNumCameras(); ++cameraId) {
+            ICamera cameraUser = null;
+            ICameraClient dummyCallbacks = new DummyCameraClient();
+
+            String clientPackageName = getContext().getPackageName();
+
+            BinderHolder holder = new BinderHolder();
+
+            try {
+                CameraBinderDecorator.newInstance(mUtils.getCameraService())
+                        .connectLegacy(dummyCallbacks, cameraId, CAMERA_HAL_API_VERSION_1_0,
+                        clientPackageName,
+                        CameraBinderTestUtils.USE_CALLING_UID, holder);
+                cameraUser = ICamera.Stub.asInterface(holder.getBinder());
+                assertNotNull(String.format("Camera %s was null", cameraId), cameraUser);
+
+                Log.v(TAG, String.format("Camera %s connected as HAL1 legacy device", cameraId));
+            } catch (RuntimeException e) {
+                // Not all camera device support openLegacy.
+                Log.i(TAG, "Unable to open camera as HAL1 legacy camera device " + e);
+            } finally {
+                if (cameraUser != null) {
+                    cameraUser.disconnect();
+                }
+            }
+        }
+    }
+
     static class DummyCameraDeviceCallbacks extends ICameraDeviceCallbacks.Stub {
 
         /*
@@ -158,6 +244,7 @@
          * android.hardware.camera2.ICameraDeviceCallbacks#onCameraError(int,
          * android.hardware.camera2.CaptureResultExtras)
          */
+        @Override
         public void onCameraError(int errorCode, CaptureResultExtras resultExtras)
                 throws RemoteException {
             // TODO Auto-generated method stub
@@ -170,6 +257,7 @@
          * android.hardware.camera2.ICameraDeviceCallbacks#onCaptureStarted(
          * android.hardware.camera2.CaptureResultExtras, long)
          */
+        @Override
         public void onCaptureStarted(CaptureResultExtras resultExtras, long timestamp)
                 throws RemoteException {
             // TODO Auto-generated method stub
@@ -183,6 +271,7 @@
          * android.hardware.camera2.impl.CameraMetadataNative,
          * android.hardware.camera2.CaptureResultExtras)
          */
+        @Override
         public void onResultReceived(CameraMetadataNative result, CaptureResultExtras resultExtras)
                 throws RemoteException {
             // TODO Auto-generated method stub
@@ -193,6 +282,7 @@
          * (non-Javadoc)
          * @see android.hardware.camera2.ICameraDeviceCallbacks#onCameraIdle()
          */
+        @Override
         public void onCameraIdle() throws RemoteException {
             // TODO Auto-generated method stub
 
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 1be2a62..6be538a 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraBinderTestUtils.java
@@ -22,6 +22,7 @@
     protected static final int INVALID_OPERATION = -38;
     protected static final int ALREADY_EXISTS = -17;
     public static final int NO_ERROR = 0;
+    public static final int EOPNOTSUPP = -95;
     private final Context mContext;
 
     public CameraBinderTestUtils(Context context) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraOpenTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraOpenTest.java
new file mode 100644
index 0000000..14bbe44
--- /dev/null
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraOpenTest.java
@@ -0,0 +1,53 @@
+/*
+ * 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.hardware.Camera;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.util.Log;
+
+/**
+ * <pre>
+ * adb shell am instrument \
+ *      -e class 'com.android.mediaframeworktest.unit.CameraOpenTest' \
+ *      -w com.android.mediaframeworktest/.MediaFrameworkUnitTestRunner
+ * </pre>
+ */
+public class CameraOpenTest extends junit.framework.TestCase {
+    private static String TAG = "CameraOpenTest";
+
+    private Camera mCamera;
+
+    /**
+     * Test @hide android.hardware.Camera#openLegacy API that cannot be tested in CTS.
+     */
+    @SmallTest
+    public void testOpenLegacy() {
+        int nCameras = Camera.getNumberOfCameras();
+        for (int id = 0; id < nCameras; id++) {
+            try {
+                mCamera.openLegacy(id, Camera.CAMERA_HAL_API_VERSION_1_0);
+            } catch (RuntimeException e) {
+                Log.i(TAG, "Unable to open camera as HAL1 legacy camera device " + e);
+            } finally {
+                if (mCamera != null) {
+                    mCamera.release();
+                }
+            }
+        }
+    }
+}
diff --git a/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml b/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
index b769a0c..acb7767 100644
--- a/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
+++ b/media/tests/ScoAudioTest/res/layout/scoaudiotest.xml
@@ -125,6 +125,11 @@
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:text="@string/audiomanagertwo" />
+        <CheckBox
+            android:id="@+id/useVirtualCallCheckBox"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:text="@string/useVirtualCallCheckText" />
             
     </LinearLayout>
 
diff --git a/media/tests/ScoAudioTest/res/values/strings.xml b/media/tests/ScoAudioTest/res/values/strings.xml
index c3ff6d5..b0284e2 100644
--- a/media/tests/ScoAudioTest/res/values/strings.xml
+++ b/media/tests/ScoAudioTest/res/values/strings.xml
@@ -10,5 +10,5 @@
     <string name="tts_speak">Speak TTS</string>
     <string name="tts_to_file">TTS to file</string>
     <string name="audiomanagertwo">Use different AudioManager for starting SCO</string>
-    
+    <string name="useVirtualCallCheckText">Use Virtual Call</string>
 </resources>
diff --git a/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java b/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
index 0304640..7e21876 100644
--- a/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
+++ b/media/tests/ScoAudioTest/src/com/android/scoaudiotest/ScoAudioTest.java
@@ -207,16 +207,25 @@
             if (mForceScoOn != isChecked) {
                 mForceScoOn = isChecked;
                 AudioManager mngr = mAudioManager;
+                boolean useVirtualCall = false;
                 CheckBox box = (CheckBox) findViewById(R.id.useSecondAudioManager);
                 if (box.isChecked()) {
                     Log.i(TAG, "Using 2nd audio manager");
                     mngr = mAudioManager2;
                 }
+                box = (CheckBox) findViewById(R.id.useVirtualCallCheckBox);
+                useVirtualCall = box.isChecked();
 
                 if (mForceScoOn) {
-                    Log.e(TAG, "startBluetoothSco() IN");
-                    mngr.startBluetoothSco();
-                    Log.e(TAG, "startBluetoothSco() OUT");
+                    if (useVirtualCall) {
+                        Log.e(TAG, "startBluetoothScoVirtualCall() IN");
+                        mngr.startBluetoothScoVirtualCall();
+                        Log.e(TAG, "startBluetoothScoVirtualCall() OUT");
+                    } else {
+                        Log.e(TAG, "startBluetoothSco() IN");
+                        mngr.startBluetoothSco();
+                        Log.e(TAG, "startBluetoothSco() OUT");
+                    }
                 } else {
                     Log.e(TAG, "stopBluetoothSco() IN");
                     mngr.stopBluetoothSco();
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
index a4d292a..229bfdb 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.cpp
@@ -105,7 +105,7 @@
     meta->findInt32(kKeyWidth, &width);
     meta->findInt32(kKeyHeight, &height);
     configBitmapSize(
-            bm, SkColorTypeToBitmapConfig(getPrefColorType(k32Bit_SrcDepth, false)),
+            bm, getPrefColorType(k32Bit_SrcDepth, false),
             width, height);
 
     // mode == DecodeBounds
@@ -169,15 +169,9 @@
     return true;
 }
 
-void OmxJpegImageDecoder::configBitmapSize(SkBitmap* bm, SkBitmap::Config pref,
+void OmxJpegImageDecoder::configBitmapSize(SkBitmap* bm, SkColorType pref,
         int width, int height) {
-    bm->setConfig(getColorSpaceConfig(pref), width, height, 0, kOpaque_SkAlphaType);
-}
-
-SkBitmap::Config OmxJpegImageDecoder::getColorSpaceConfig(
-        SkBitmap::Config pref) {
-
-    // Set the color space to ARGB_8888 for now
+    // Set the color space to ARGB_8888 for now (ignoring pref)
     // because of limitation in hardware support.
-    return SkBitmap::kARGB_8888_Config;
+    bm->setInfo(SkImageInfo::MakeN32(width, height, kOpaque_SkAlphaType));
 }
diff --git a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
index e431e72..e487245 100644
--- a/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
+++ b/media/tests/omxjpegdecoder/omx_jpeg_decoder.h
@@ -49,9 +49,7 @@
     sp<MediaSource> getDecoder(OMXClient* client, const sp<MediaSource>& source);
     bool decodeSource(sp<MediaSource> decoder, const sp<MediaSource>& source,
             SkBitmap* bm);
-    void configBitmapSize(SkBitmap* bm, SkBitmap::Config pref, int width,
-            int height);
-    SkBitmap::Config getColorSpaceConfig(SkBitmap::Config pref);
+    void configBitmapSize(SkBitmap* bm, SkColorType, int width, int height);
 
     OMXClient mClient;
 };
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 2ed3d73..52db30a 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -27,6 +27,7 @@
 import android.content.pm.PackageInfoLite;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
 import android.content.res.ObbInfo;
 import android.content.res.ObbScanner;
 import android.net.Uri;
@@ -157,6 +158,7 @@
          * @return Returns PackageInfoLite object containing
          * the package info and recommended app location.
          */
+        @Override
         public PackageInfoLite getMinimalPackageInfo(final String packagePath, int flags,
                 long threshold, String abiOverride) {
             PackageInfoLite ret = new PackageInfoLite();
@@ -167,14 +169,13 @@
                 return ret;
             }
 
-            DisplayMetrics metrics = new DisplayMetrics();
-            metrics.setToDefaults();
-
-            PackageParser.ApkLite pkg = PackageParser.parseApkLite(packagePath, 0);
-            if (pkg == null) {
+            final File apkFile = new File(packagePath);
+            final PackageParser.ApkLite pkg;
+            try {
+                pkg = PackageParser.parseApkLite(apkFile, 0);
+            } catch (PackageParserException e) {
                 Slog.w(TAG, "Failed to parse package");
 
-                final File apkFile = new File(packagePath);
                 if (!apkFile.exists()) {
                     ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_URI;
                 } else {
diff --git a/packages/DocumentsUI/res/values-km-rKH/strings.xml b/packages/DocumentsUI/res/values-km-rKH/strings.xml
index 8c9030d..e8944ec 100644
--- a/packages/DocumentsUI/res/values-km-rKH/strings.xml
+++ b/packages/DocumentsUI/res/values-km-rKH/strings.xml
@@ -20,14 +20,14 @@
     <string name="title_open" msgid="4353228937663917801">"បើក​ពី"</string>
     <string name="title_save" msgid="2433679664882857999">"រក្សា​ទុក​ទៅ"</string>
     <string name="menu_create_dir" msgid="5947289605844398389">"បង្កើត​ថត"</string>
-    <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា​"</string>
+    <string name="menu_grid" msgid="6878021334497835259">"ទិដ្ឋភាព​ក្រឡា"</string>
     <string name="menu_list" msgid="7279285939892417279">"ទិដ្ឋភាព​បញ្ជី"</string>
     <string name="menu_sort" msgid="7677740407158414452">"តម្រៀប​តាម"</string>
     <string name="menu_search" msgid="3816712084502856974">"ស្វែងរក"</string>
     <string name="menu_settings" msgid="6008033148948428823">"ការ​កំណត់"</string>
     <string name="menu_open" msgid="432922957274920903">"បើក"</string>
     <string name="menu_save" msgid="2394743337684426338">"រក្សាទុក"</string>
-    <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក​"</string>
+    <string name="menu_share" msgid="3075149983979628146">"ចែករំលែក"</string>
     <string name="menu_delete" msgid="8138799623850614177">"លុប"</string>
     <string name="menu_select" msgid="8711270657353563424">"ជ្រើស \"<xliff:g id="DIRECTORY">^1</xliff:g>\""</string>
     <string name="mode_selected_count" msgid="459111894725594625">"បាន​ជ្រើស <xliff:g id="COUNT">%1$d</xliff:g>"</string>
@@ -48,7 +48,7 @@
     <string name="pref_advanced_devices" msgid="903257239609301276">"បង្ហាញ​ឧបករណ៍​កម្រិត​ខ្ពស់"</string>
     <string name="pref_file_size" msgid="2826879315743961459">"បង្ហាញ​ទំហំ​ឯកសារ"</string>
     <string name="pref_device_size" msgid="3542106883278997222">"បង្ហាញ​ទំហំ​ឧបករណ៍"</string>
-    <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ​"</string>
+    <string name="empty" msgid="7858882803708117596">"គ្មានធាតុ"</string>
     <string name="toast_no_application" msgid="1339885974067891667">"មិន​អាច​បើក​ឯកសារ"</string>
     <string name="toast_failed_delete" msgid="2180678019407244069">"មិន​អាច​លុប​ឯកសារ​មួយ​ចំនួន"</string>
     <string name="share_via" msgid="8966594246261344259">"ចែករំលែក​តាម"</string>
diff --git a/packages/Keyguard/res/values-ca/strings.xml b/packages/Keyguard/res/values-ca/strings.xml
index d23d487..f258224 100644
--- a/packages/Keyguard/res/values-ca/strings.xml
+++ b/packages/Keyguard/res/values-ca/strings.xml
@@ -59,7 +59,7 @@
     <string name="keyguard_accessibility_widget_reorder_start" msgid="8736853615588828197">"S\'ha iniciat la reorganització del widget."</string>
     <string name="keyguard_accessibility_widget_reorder_end" msgid="7170190950870468320">"Ha finalitzat la reorganització del widget."</string>
     <string name="keyguard_accessibility_widget_deleted" msgid="4426204263929224434">"S\'ha suprimit el widget de <xliff:g id="WIDGET_INDEX">%1$s</xliff:g>."</string>
-    <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Amplia l\'àrea de desbloqueig."</string>
+    <string name="keyguard_accessibility_expand_lock_area" msgid="519859720934178024">"Desplega l\'àrea de desbloqueig."</string>
     <string name="keyguard_accessibility_slide_unlock" msgid="2959928478764697254">"Desbloqueig lliscant el dit"</string>
     <string name="keyguard_accessibility_pattern_unlock" msgid="1490840706075246612">"Desbloqueig mitjançant patró"</string>
     <string name="keyguard_accessibility_face_unlock" msgid="4817282543351718535">"Desbloqueig facial"</string>
diff --git a/packages/Keyguard/res/values-km-rKH/strings.xml b/packages/Keyguard/res/values-km-rKH/strings.xml
index c26b1b4..ecdad8c 100644
--- a/packages/Keyguard/res/values-km-rKH/strings.xml
+++ b/packages/Keyguard/res/values-km-rKH/strings.xml
@@ -83,7 +83,7 @@
     <string name="password_keyboard_label_alpha_key" msgid="8001096175167485649">"ABC"</string>
     <string name="password_keyboard_label_alt_key" msgid="1284820942620288678">"ALT"</string>
     <string name="keyboardview_keycode_alt" msgid="4856868820040051939">"Alt"</string>
-    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់​"</string>
+    <string name="keyboardview_keycode_cancel" msgid="1203984017245783244">"បោះ​បង់"</string>
     <string name="keyboardview_keycode_delete" msgid="3337914833206635744">"លុប"</string>
     <string name="keyboardview_keycode_done" msgid="1992571118466679775">"រួចរាល់"</string>
     <string name="keyboardview_keycode_mode_change" msgid="4547387741906537519">"ប្ដូរ​របៀប"</string>
@@ -120,7 +120,7 @@
     <string name="kg_login_too_many_attempts" msgid="6486842094005698475">"ព្យាយាម​លំនាំ​ច្រើន​ពេក"</string>
     <string name="kg_login_instructions" msgid="1100551261265506448">"ដើម្បី​ដោះ​សោ ចូល​ក្នុង​គណនី Google ។"</string>
     <string name="kg_login_username_hint" msgid="5718534272070920364">"ឈ្មោះ​អ្នក​ប្រើ (អ៊ី​ម៉ែ​ល​)"</string>
-    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់​"</string>
+    <string name="kg_login_password_hint" msgid="9057289103827298549">"ពាក្យសម្ងាត់"</string>
     <string name="kg_login_submit_button" msgid="5355904582674054702">"ចូល"</string>
     <string name="kg_login_invalid_input" msgid="5754664119319872197">"ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​មិន​ត្រឹមត្រូវ។"</string>
     <string name="kg_login_account_recovery_hint" msgid="5690709132841752974">"ភ្លេច​ឈ្មោះ​អ្នកប្រើ ឬ​ពាក្យ​សម្ងាត់​របស់​អ្នក?\nមើល "<b>"google.com/accounts/recovery"</b>" ។"</string>
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index d2bf30c..ab18271 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -71,6 +71,7 @@
     private AppWidgetHost mAppWidgetHost;
     private AppWidgetManager mAppWidgetManager;
     private KeyguardWidgetPager mAppWidgetContainer;
+    // TODO remove transport control references, these don't exist anymore
     private KeyguardTransportControlView mTransportControl;
     private int mAppWidgetToShow;
 
@@ -235,36 +236,6 @@
                 mKeyguardMultiUserSelectorView.finalizeActiveUserView(true);
             }
         }
-        @Override
-        public void onMusicClientIdChanged(
-                int clientGeneration, boolean clearing, android.app.PendingIntent intent) {
-            // Set transport state to invisible until we know music is playing (below)
-            if (DEBUGXPORT && (mClientGeneration != clientGeneration || clearing)) {
-                Log.v(TAG, (clearing ? "hide" : "show") + " transport, gen:" + clientGeneration);
-            }
-            mClientGeneration = clientGeneration;
-            final int newState = (clearing ? TRANSPORT_GONE
-                    : (mTransportState == TRANSPORT_VISIBLE ?
-                    TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
-            if (newState != mTransportState) {
-                mTransportState = newState;
-                if (DEBUGXPORT) Log.v(TAG, "update widget: transport state changed");
-                KeyguardHostView.this.post(mSwitchPageRunnable);
-            }
-        }
-        @Override
-        public void onMusicPlaybackStateChanged(int playbackState, long eventTime) {
-            if (DEBUGXPORT) Log.v(TAG, "music state changed: " + playbackState);
-            if (mTransportState != TRANSPORT_GONE) {
-                final int newState = (isMusicPlaying(playbackState) ?
-                        TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE);
-                if (newState != mTransportState) {
-                    mTransportState = newState;
-                    if (DEBUGXPORT) Log.v(TAG, "update widget: play state changed");
-                    KeyguardHostView.this.post(mSwitchPageRunnable);
-                }
-            }
-        }
     };
 
     private static final boolean isMusicPlaying(int playbackState) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
index 7918755..38316ff 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardStatusView.java
@@ -166,6 +166,11 @@
         return info;
     }
 
+    @Override
+    public boolean hasOverlappingRendering() {
+        return false;
+    }
+
     // DateFormat.getBestDateTimePattern is extremely expensive, and refresh is called often.
     // This is an optimization to ensure we only recompute the patterns when the inputs change.
     private static final class Patterns {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 668e1ef..bf34705 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -91,8 +91,6 @@
     private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312;
     protected static final int MSG_BOOT_COMPLETED = 313;
     private static final int MSG_USER_SWITCH_COMPLETE = 314;
-    private static final int MSG_SET_CURRENT_CLIENT_ID = 315;
-    protected static final int MSG_SET_PLAYBACK_STATE = 316;
     protected static final int MSG_USER_INFO_CHANGED = 317;
     protected static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
     private static final int MSG_SCREEN_TURNED_ON = 319;
@@ -184,12 +182,6 @@
                 case MSG_BOOT_COMPLETED:
                     handleBootCompleted();
                     break;
-                case MSG_SET_CURRENT_CLIENT_ID:
-                    handleSetGenerationId(msg.arg1, msg.arg2 != 0, (PendingIntent) msg.obj);
-                    break;
-                case MSG_SET_PLAYBACK_STATE:
-                    handleSetPlaybackState(msg.arg1, msg.arg2, (Long) msg.obj);
-                    break;
                 case MSG_USER_INFO_CHANGED:
                     handleUserInfoChanged(msg.arg1);
                     break;
@@ -206,8 +198,6 @@
         }
     };
 
-    private AudioManager mAudioManager;
-
     private SparseBooleanArray mUserHasTrust = new SparseBooleanArray();
 
     @Override
@@ -257,49 +247,6 @@
 
     private DisplayClientState mDisplayClientState = new DisplayClientState();
 
-    /**
-     * This currently implements the bare minimum required to enable showing and hiding
-     * KeyguardTransportControl.  There's a lot of client state to maintain which is why
-     * KeyguardTransportControl maintains an independent connection while it's showing.
-     */
-    private final IRemoteControlDisplay.Stub mRemoteControlDisplay =
-                new IRemoteControlDisplay.Stub() {
-
-        public void setPlaybackState(int generationId, int state, long stateChangeTimeMs,
-                long currentPosMs, float speed) {
-            Message msg = mHandler.obtainMessage(MSG_SET_PLAYBACK_STATE,
-                    generationId, state, stateChangeTimeMs);
-            mHandler.sendMessage(msg);
-        }
-
-        public void setMetadata(int generationId, Bundle metadata) {
-
-        }
-
-        public void setTransportControlInfo(int generationId, int flags, int posCapabilities) {
-
-        }
-
-        public void setArtwork(int generationId, Bitmap bitmap) {
-
-        }
-
-        public void setAllMetadata(int generationId, Bundle metadata, Bitmap bitmap) {
-
-        }
-
-        public void setEnabled(boolean enabled) {
-            // no-op: this RemoteControlDisplay is not subject to being disabled.
-        }
-
-        public void setCurrentClientId(int clientGeneration, PendingIntent mediaIntent,
-                boolean clearing) throws RemoteException {
-            Message msg = mHandler.obtainMessage(MSG_SET_CURRENT_CLIENT_ID,
-                        clientGeneration, (clearing ? 1 : 0), mediaIntent);
-            mHandler.sendMessage(msg);
-        }
-    };
-
     private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
 
         public void onReceive(Context context, Intent intent) {
@@ -501,38 +448,6 @@
         }
     }
 
-    protected void handleSetGenerationId(int clientGeneration, boolean clearing, PendingIntent p) {
-        mDisplayClientState.clientGeneration = clientGeneration;
-        mDisplayClientState.clearing = clearing;
-        mDisplayClientState.intent = p;
-        if (DEBUG)
-            Log.v(TAG, "handleSetGenerationId(g=" + clientGeneration + ", clear=" + clearing + ")");
-        for (int i = 0; i < mCallbacks.size(); i++) {
-            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
-            if (cb != null) {
-                cb.onMusicClientIdChanged(clientGeneration, clearing, p);
-            }
-        }
-    }
-
-    protected void handleSetPlaybackState(int generationId, int playbackState, long eventTime) {
-        if (DEBUG)
-            Log.v(TAG, "handleSetPlaybackState(gen=" + generationId
-                + ", state=" + playbackState + ", t=" + eventTime + ")");
-        mDisplayClientState.playbackState = playbackState;
-        mDisplayClientState.playbackEventTime = eventTime;
-        if (generationId == mDisplayClientState.clientGeneration) {
-            for (int i = 0; i < mCallbacks.size(); i++) {
-                KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
-                if (cb != null) {
-                    cb.onMusicPlaybackStateChanged(playbackState, eventTime);
-                }
-            }
-        } else {
-            Log.w(TAG, "Ignoring generation id " + generationId + " because it's not current");
-        }
-    }
-
     private void handleUserInfoChanged(int userId) {
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -694,8 +609,6 @@
     protected void handleBootCompleted() {
         if (mBootCompleted) return;
         mBootCompleted = true;
-        mAudioManager = new AudioManager(mContext);
-        mAudioManager.registerRemoteControlDisplay(mRemoteControlDisplay);
         for (int i = 0; i < mCallbacks.size(); i++) {
             KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
             if (cb != null) {
@@ -1013,12 +926,6 @@
         callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn);
         callback.onClockVisibilityChanged();
         callback.onSimStateChanged(mSimState);
-        callback.onMusicClientIdChanged(
-                mDisplayClientState.clientGeneration,
-                mDisplayClientState.clearing,
-                mDisplayClientState.intent);
-        callback.onMusicPlaybackStateChanged(mDisplayClientState.playbackState,
-                mDisplayClientState.playbackEventTime);
     }
 
     public void sendKeyguardVisibilityChanged(boolean showing) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index bcdf18f..01600d2 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -144,18 +144,6 @@
     public void onBootCompleted() { }
 
     /**
-     * Called when audio client attaches or detaches from AudioManager.
-     */
-    public void onMusicClientIdChanged(int clientGeneration, boolean clearing, PendingIntent intent) { }
-
-    /**
-     * Called when the audio playback state changes.
-     * @param playbackState
-     * @param eventTime
-     */
-    public void onMusicPlaybackStateChanged(int playbackState, long eventTime) { }
-
-    /**
      * Called when the emergency call button is pressed.
      */
     void onEmergencyCallAction() { }
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
index 3e444fa..8945b15 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardViewBase.java
@@ -341,11 +341,11 @@
                         }
                         // Volume buttons should only function for music (local or remote).
                         // TODO: Actually handle MUTE.
-                        mAudioManager.adjustLocalOrRemoteStreamVolume(
-                                AudioManager.STREAM_MUSIC,
+                        mAudioManager.adjustSuggestedStreamVolume(
                                 keyCode == KeyEvent.KEYCODE_VOLUME_UP
                                         ? AudioManager.ADJUST_RAISE
-                                        : AudioManager.ADJUST_LOWER);
+                                        : AudioManager.ADJUST_LOWER /* direction */,
+                                AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */);
                         // Don't execute default volume behavior
                         return true;
                     } else {
@@ -376,17 +376,13 @@
     }
 
     private void handleMediaKeyEvent(KeyEvent keyEvent) {
-        IAudioService audioService = IAudioService.Stub.asInterface(
-                ServiceManager.checkService(Context.AUDIO_SERVICE));
-        if (audioService != null) {
-            try {
-                audioService.dispatchMediaKeyEvent(keyEvent);
-            } catch (RemoteException e) {
-                Log.e("KeyguardViewBase", "dispatchMediaKeyEvent threw exception " + e);
+        synchronized (this) {
+            if (mAudioManager == null) {
+                mAudioManager = (AudioManager) getContext().getSystemService(
+                        Context.AUDIO_SERVICE);
             }
-        } else {
-            Slog.w("KeyguardViewBase", "Unable to find IAudioService for media key event");
         }
+        mAudioManager.dispatchMediaKeyEvent(keyEvent);
     }
 
     @Override
diff --git a/packages/PrintSpooler/res/values-km-rKH/strings.xml b/packages/PrintSpooler/res/values-km-rKH/strings.xml
index cdcb21f..0146ab7 100644
--- a/packages/PrintSpooler/res/values-km-rKH/strings.xml
+++ b/packages/PrintSpooler/res/values-km-rKH/strings.xml
@@ -60,7 +60,7 @@
   </plurals>
     <string name="cancel" msgid="4373674107267141885">"បោះបង់"</string>
     <string name="restart" msgid="2472034227037808749">"ចាប់ផ្ដើម​ឡើងវិញ"</string>
-    <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព​"</string>
+    <string name="no_connection_to_printer" msgid="2159246915977282728">"គ្មាន​​​ការ​ភ្ជាប់​ទៅ​ម៉ាស៊ីន​បោះពុម្ព"</string>
     <string name="reason_unknown" msgid="5507940196503246139">"មិន​ស្គាល់"</string>
     <string name="printer_unavailable" msgid="2434170617003315690">"<xliff:g id="PRINT_JOB_NAME">%1$s</xliff:g> – មិន​អាច​ប្រើ​បាន"</string>
   <string-array name="color_mode_labels">
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
index d4fdbf3..17100f7 100644
--- a/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-hdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..e969d4c2
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
index 9fc1a3b..b53bd8f 100644
--- a/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-mdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..657f710
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
index f38de93..09606f6 100644
--- a/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..a444c55
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
index 8194605..427cad9 100644
--- a/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_lower_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png b/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png
new file mode 100644
index 0000000..29cf44b
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/recents_status_gradient.9.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_account_circle.xml b/packages/SystemUI/res/drawable/ic_account_circle.xml
index a7e8514..4a4c1c1 100644
--- a/packages/SystemUI/res/drawable/ic_account_circle.xml
+++ b/packages/SystemUI/res/drawable/ic_account_circle.xml
@@ -22,7 +22,13 @@
         android:viewportWidth="24.0"
         android:viewportHeight="24.0"/>
 
+    <group
+        android:scaleX="1.2"
+        android:scaleY="1.2"
+        android:pivotX="12.0"
+        android:pivotY="12.0">
     <path
         android:fill="#FFFFFFFF"
         android:pathData="M12.0,2.0C6.5,2.0 2.0,6.5 2.0,12.0s4.5,10.0 10.0,10.0c5.5,0.0 10.0,-4.5 10.0,-10.0S17.5,2.0 12.0,2.0zM12.0,5.0c1.7,0.0 3.0,1.3 3.0,3.0c0.0,1.7 -1.3,3.0 -3.0,3.0c-1.7,0.0 -3.0,-1.3 -3.0,-3.0C9.0,6.3 10.3,5.0 12.0,5.0zM12.0,19.2c-2.5,0.0 -4.7,-1.3 -6.0,-3.2c0.0,-2.0 4.0,-3.1 6.0,-3.1c2.0,0.0 6.0,1.1 6.0,3.1C16.7,17.9 14.5,19.2 12.0,19.2z"/>
+    </group>
 </vector>
diff --git a/packages/SystemUI/res/drawable/ic_notify_zen.xml b/packages/SystemUI/res/drawable/ic_notify_zen.xml
deleted file mode 100644
index c46455b..0000000
--- a/packages/SystemUI/res/drawable/ic_notify_zen.xml
+++ /dev/null
@@ -1,28 +0,0 @@
-<!--
-Copyright (C) 2014 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.
--->
-<vector xmlns:android="http://schemas.android.com/apk/res/android" >
-    <size
-        android:width="24dp"
-        android:height="24dp"/>
-
-    <viewport
-        android:viewportWidth="48.0"
-        android:viewportHeight="48.0"/>
-
-    <path
-        android:fill="#FFFFFFFF"
-        android:pathData="M4.0,24.0c0.0,11.0 9.0,20.0 20.0,20.0s20.0,-9.0 20.0,-20.0S35.0,4.0 24.0,4.0S4.0,13.0 4.0,24.0zM36.6,33.8L14.2,11.4C16.9,9.3 20.3,8.0 24.0,8.0c8.8,0.0 16.0,7.2 16.0,16.0C40.0,27.7 38.7,31.1 36.6,33.8zM8.0,24.0c0.0,-3.7 1.3,-7.1 3.4,-9.8L33.8,36.6C31.1,38.7 27.7,40.0 24.0,40.0C15.2,40.0 8.0,32.8 8.0,24.0z"/>
-</vector>
diff --git a/packages/SystemUI/res/drawable/notification_scrim.xml b/packages/SystemUI/res/drawable/notification_scrim.xml
new file mode 100644
index 0000000..ff7e31f1
--- /dev/null
+++ b/packages/SystemUI/res/drawable/notification_scrim.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2014 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
+  -->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <solid android:color="#34000000" />
+    <corners android:radius="@*android:dimen/notification_material_rounded_rect_radius" />
+</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_button_bg.xml b/packages/SystemUI/res/drawable/recents_button_bg.xml
new file mode 100644
index 0000000..a4cb088
--- /dev/null
+++ b/packages/SystemUI/res/drawable/recents_button_bg.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="?android:attr/colorControlHighlight" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher.xml b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
new file mode 100644
index 0000000..5648065
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2014 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:id="@+id/keyguard_user_switcher"
+        android:orientation="vertical"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:gravity="end"
+        android:visibility="gone"
+        android:paddingTop="4dp">
+</LinearLayout>
diff --git a/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
new file mode 100644
index 0000000..691a80e
--- /dev/null
+++ b/packages/SystemUI/res/layout/keyguard_user_switcher_item.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+  ~ Copyright (C) 2014 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"
+        xmlns:sysui="http://schemas.android.com/apk/res-auto"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="8dp"
+        android:layout_marginEnd="8dp"
+        android:gravity="center_vertical"
+        android:clickable="true"
+        android:background="@drawable/ripple_drawable">
+    <TextView android:id="@+id/name"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginEnd="16dp"
+            android:textAppearance="@style/TextAppearance.StatusBar.Expanded.UserSwitcher.UserName"
+            />
+    <com.android.systemui.statusbar.phone.UserAvatarView android:id="@+id/picture"
+            android:layout_width="48dp"
+            android:layout_height="48dp"
+            android:contentDescription="@null"
+            sysui:frameWidth="@dimen/keyguard_user_switcher_border_thickness"
+            sysui:activeFrameColor="@color/current_user_border_color" />
+</LinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_status_bar_scrim.xml b/packages/SystemUI/res/layout/recents_status_bar_scrim.xml
new file mode 100644
index 0000000..24928d0
--- /dev/null
+++ b/packages/SystemUI/res/layout/recents_status_bar_scrim.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<ImageView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center_horizontal|top"
+    android:scaleType="fitXY"
+    android:src="@drawable/recents_status_gradient" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 23f2796..1bab67a 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -32,8 +32,10 @@
             android:id="@+id/application_icon"
             android:layout_width="@dimen/recents_task_view_application_icon_size"
             android:layout_height="@dimen/recents_task_view_application_icon_size"
-            android:layout_marginStart="16dp"
-            android:layout_gravity="center_vertical|start" />
+            android:layout_marginStart="8dp"
+            android:layout_gravity="center_vertical|start"
+            android:padding="8dp"
+            android:background="@drawable/recents_button_bg" />
         <TextView
             android:id="@+id/activity_description"
             android:layout_width="match_parent"
@@ -56,6 +58,7 @@
             android:layout_marginEnd="4dp"
             android:layout_gravity="center_vertical|end"
             android:padding="18dp"
+            android:background="@drawable/recents_button_bg"
             android:visibility="invisible"
             android:src="@drawable/recents_dismiss_light" />
     </com.android.systemui.recents.views.TaskBarView>
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index cde83bf..b54ba1a 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -24,6 +24,7 @@
     android:id="@+id/notification_panel"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:background="@android:color/transparent"
     >
 
     <include
@@ -91,6 +92,14 @@
 
     <include layout="@layout/status_bar_expanded_header" />
 
+    <ViewStub
+        android:id="@+id/keyguard_user_switcher"
+        android:layout_height="wrap_content"
+        android:layout_width="wrap_content"
+        android:layout_marginTop="@dimen/status_bar_header_height_keyguard"
+        android:layout_gravity="end"
+        android:layout="@layout/keyguard_user_switcher" />
+
     <include
         layout="@layout/keyguard_bottom_area"
         android:visibility="gone" />
diff --git a/packages/SystemUI/res/layout/status_bar_expanded_header.xml b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
index 353368b..2e4c0ef 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded_header.xml
@@ -73,9 +73,9 @@
         android:layout_width="wrap_content"
         android:layout_height="@dimen/status_bar_header_height"
         android:layout_toStartOf="@id/multi_user_switch"
-        android:layout_marginEnd="2dp"
+        android:layout_alignWithParentIfMissing="true"
         android:layout_marginStart="16dp"
-        />
+        android:paddingEnd="2dp" />
 
     <TextView
         android:id="@+id/header_charging_info"
diff --git a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
index c442f79..f0f50e1 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_keyguard_overflow.xml
@@ -49,4 +49,10 @@
         android:layout_width="120dp"
         android:layout_height="wrap_content"
         />
+
+    <com.android.systemui.statusbar.NotificationScrimView
+        android:id="@+id/scrim_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
 </com.android.systemui.statusbar.NotificationOverflowContainer>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 5fabd3e..7663d54 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -48,4 +48,9 @@
         android:padding="2dp"
         />
 
+    <com.android.systemui.statusbar.NotificationScrimView
+        android:id="@+id/scrim_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
 </com.android.systemui.statusbar.ExpandableNotificationRow>
diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml
index 19b0f17..019fea0 100644
--- a/packages/SystemUI/res/values-fi/strings.xml
+++ b/packages/SystemUI/res/values-fi/strings.xml
@@ -243,10 +243,8 @@
     <string name="camera_hint" msgid="5241441720959174226">"Avaa kamera pyyhkäisemällä oikealle"</string>
     <string name="zen_mode_forever" msgid="7420011936770086993">"Kunnes poistat tämän käytöstä"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Ladataan (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> kunnes täynnä)"</string>
-    <!-- no translation found for guest_nickname (8059989128963789678) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (4259024453643879653) -->
-    <skip />
+    <string name="guest_nickname" msgid="8059989128963789678">"Vieras"</string>
+    <string name="guest_new_guest" msgid="4259024453643879653">"+ Vieras"</string>
   <plurals name="zen_mode_duration_minutes">
     <item quantity="one" msgid="9040808414992812341">"Minuutiksi"</item>
     <item quantity="other" msgid="6924190729213550991">"%d minuutiksi"</item>
diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml
index fcad161..d11c9be 100644
--- a/packages/SystemUI/res/values-fr/strings.xml
+++ b/packages/SystemUI/res/values-fr/strings.xml
@@ -244,7 +244,7 @@
     <string name="phone_hint" msgid="3101468054914424646">"Balayer l\'écran vers la droite pour accéder au téléphone"</string>
     <string name="camera_hint" msgid="5241441720959174226">"Balayer l\'écran vers la gauche pour accéder à l\'appareil photo"</string>
     <string name="zen_mode_forever" msgid="7420011936770086993">"Jusqu\'à la désactivation"</string>
-    <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours… (chargé à 100 % dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
+    <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Charge en cours… (chargé à 100 %% dans <xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>)"</string>
     <string name="guest_nickname" msgid="8059989128963789678">"Invité"</string>
     <string name="guest_new_guest" msgid="4259024453643879653">"Ajouter un invité"</string>
   <plurals name="zen_mode_duration_minutes">
diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml
index 9f1722d..ddb7669 100644
--- a/packages/SystemUI/res/values-it/strings.xml
+++ b/packages/SystemUI/res/values-it/strings.xml
@@ -40,7 +40,7 @@
     <string name="invalid_charger_text" msgid="5474997287953892710">"Utilizza solo il caricabatterie fornito in dotazione."</string>
     <string name="battery_low_why" msgid="4553600287639198111">"Impostazioni"</string>
     <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Avviare risparmio batteria?"</string>
-    <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Inizia"</string>
+    <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Avvia"</string>
     <string name="battery_saver_start_action" msgid="7245333922937402896">"Avvia risparmio batteria"</string>
     <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Per aumentare la durata della batteria, Risparmio batteria riduce le prestazioni del tuo dispositivo.\n\nRisparmio batteria si disattiva quando il dispositivo è collegato alla corrente."</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Impostazioni"</string>
diff --git a/packages/SystemUI/res/values-ka-rGE/strings.xml b/packages/SystemUI/res/values-ka-rGE/strings.xml
index 8f1f30a..a35c821 100644
--- a/packages/SystemUI/res/values-ka-rGE/strings.xml
+++ b/packages/SystemUI/res/values-ka-rGE/strings.xml
@@ -42,7 +42,7 @@
     <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"გსურთ ბატარეის დამზოგის დაწყება?"</string>
     <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"დაწყება"</string>
     <string name="battery_saver_start_action" msgid="7245333922937402896">"ბატარეის დამზოგის დაწყება"</string>
-    <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"ბატარეის მოქმედების გასახანგრძლივებლად, ბატარეის დამზოგი შეამცირებს თქვენი მოწყობილობის წარმადობას.\n\nბატარეის დამზოგი გამოირთვება, როდესაც მოწყობილობას ელკვებაზე მიაერთებთ."</string>
+    <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"ბატარეის მოქმედების გასახანგრძლივებლად ბატარეის დამზოგი შეამცირებს თქვენი მოწყობილობის წარმადობას.\n\nბატარეის დამზოგი გამოირთვება, როდესაც მოწყობილობას ელკვებაზე მიაერთებთ."</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"პარამეტრები"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
     <string name="status_bar_settings_airplane" msgid="4879879698500955300">"თვითმფრინავის რეჟიმი"</string>
diff --git a/packages/SystemUI/res/values-km-rKH/strings.xml b/packages/SystemUI/res/values-km-rKH/strings.xml
index 88d9b64..29c5e5c 100644
--- a/packages/SystemUI/res/values-km-rKH/strings.xml
+++ b/packages/SystemUI/res/values-km-rKH/strings.xml
@@ -70,7 +70,7 @@
     <string name="screenshot_saving_ticker" msgid="7403652894056693515">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់…"</string>
     <string name="screenshot_saving_title" msgid="8242282144535555697">"កំពុង​រក្សាទុក​រូបថត​អេក្រង់..."</string>
     <string name="screenshot_saving_text" msgid="2419718443411738818">"រូបថត​អេក្រង់​កំពុង​ត្រូវ​បាន​រក្សាទុក។"</string>
-    <string name="screenshot_saved_title" msgid="6461865960961414961">"បាន​ចាប់​យក​រូបថត​អេក្រង់។​"</string>
+    <string name="screenshot_saved_title" msgid="6461865960961414961">"បាន​ចាប់​យក​រូបថត​អេក្រង់។"</string>
     <string name="screenshot_saved_text" msgid="1152839647677558815">"ប៉ះ ​ដើម្បី​មើល​រូបថត​អេក្រង់​របស់​អ្នក​។"</string>
     <string name="screenshot_failed_title" msgid="705781116746922771">"មិន​អាច​ចាប់​យក​រូប​ថត​អេក្រង់​។"</string>
     <string name="screenshot_failed_text" msgid="8134011269572415402">"មិន​អាច​រក្សាទុក​រូបថត​អេក្រង់​។ ឧបករណ៍​ផ្ទុក​អាច​កំពុង​ប្រើ​​។"</string>
@@ -147,7 +147,7 @@
     <string name="accessibility_remove_notification" msgid="3603099514902182350">"សម្អាត​ការ​ជូន​ដំណឹង។"</string>
     <string name="accessibility_gps_enabled" msgid="3511469499240123019">"បាន​បើក GPS ។"</string>
     <string name="accessibility_gps_acquiring" msgid="8959333351058967158">"ទទួល​​ GPS ។"</string>
-    <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បាន​បើក​ម៉ាស៊ីន​អង្គុលីលេខ​"</string>
+    <string name="accessibility_tty_enabled" msgid="4613200365379426561">"បាន​បើក​ម៉ាស៊ីន​អង្គុលីលេខ"</string>
     <string name="accessibility_ringer_vibrate" msgid="666585363364155055">"កម្មវិធី​រោទ៍​ញ័រ។"</string>
     <string name="accessibility_ringer_silent" msgid="9061243307939135383">"កម្មវិធី​រោទ៍​ស្ងាត់។"</string>
     <string name="accessibility_recents_item_dismissed" msgid="6803574935084867070">"<xliff:g id="APP">%s</xliff:g> បដិសេធ។"</string>
@@ -197,7 +197,7 @@
     <string name="quick_settings_rotation_locked_portrait_label" msgid="5102691921442135053">"បញ្ឈរ"</string>
     <string name="quick_settings_rotation_locked_landscape_label" msgid="8553157770061178719">"ទេសភាព"</string>
     <string name="quick_settings_ime_label" msgid="7073463064369468429">"វិធីសាស្ត្រ​បញ្ចូល"</string>
-    <string name="quick_settings_location_label" msgid="5011327048748762257">"ទី​តាំង​"</string>
+    <string name="quick_settings_location_label" msgid="5011327048748762257">"ទី​តាំង"</string>
     <string name="quick_settings_location_off_label" msgid="7464544086507331459">"ទីតាំង​បាន​បិទ"</string>
     <string name="quick_settings_media_device_label" msgid="1302906836372603762">"ឧបករណ៍​មេឌៀ"</string>
     <string name="quick_settings_rssi_label" msgid="7725671335550695589">"RSSI"</string>
@@ -221,11 +221,11 @@
     <string name="recents_empty_message" msgid="7883614615463619450">"មិនមាន​​កម្មវិធី​ថ្មីៗ"</string>
     <string name="recents_app_info_button_label" msgid="2890317189376000030">"ព័ត៌មាន​កម្មវិធី"</string>
     <string name="recents_search_bar_label" msgid="8074997400187836677">"ស្វែងរក"</string>
-    <string name="expanded_header_battery_charged" msgid="5945855970267657951">"បាន​បញ្ចូល​ថ្ម​​"</string>
+    <string name="expanded_header_battery_charged" msgid="5945855970267657951">"បាន​បញ្ចូល​ថ្ម"</string>
     <string name="expanded_header_battery_charging" msgid="205623198487189724">"កំពុង​បញ្ចូល​ថ្ម"</string>
     <string name="expanded_header_battery_charging_with_time" msgid="457559884275395376">"<xliff:g id="CHARGING_TIME">%s</xliff:g> រហូត​ដល់ពេញ"</string>
     <string name="expanded_header_battery_not_charging" msgid="4798147152367049732">"មិន​កំពុង​បញ្ចូល​ថ្ម"</string>
-    <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញ​អាច​\nត្រូវ​បាន​ត្រួតពិនិត្យ​"</string>
+    <string name="ssl_ca_cert_warning" msgid="9005954106902053641">"បណ្ដាញ​អាច​\nត្រូវ​បាន​ត្រួតពិនិត្យ"</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_left" msgid="7207478719805562165">"រុញ​ទៅ​ឆ្វេង​ដើម្បី <xliff:g id="TARGET_DESCRIPTION">%s</xliff:g> ។"</string>
diff --git a/packages/SystemUI/res/values-lo-rLA/strings.xml b/packages/SystemUI/res/values-lo-rLA/strings.xml
index 3688c57..b765fb72 100644
--- a/packages/SystemUI/res/values-lo-rLA/strings.xml
+++ b/packages/SystemUI/res/values-lo-rLA/strings.xml
@@ -241,7 +241,7 @@
     <string name="keyguard_unlock" msgid="8043466894212841998">"ເລື່ອນ​ຂຶ້ນ​ເພື່ອ​ປົດ​ລັອກ"</string>
     <string name="phone_hint" msgid="3101468054914424646">"ປັດ​ຂວາ​ເພື່ອ​ໃຊ້​ໂທ​ລະ​ສັບ"</string>
     <string name="camera_hint" msgid="5241441720959174226">"ປັດ​ຊ້າຍ​ເພື່ອ​ໃຊ້​ກ້ອງ"</string>
-    <string name="zen_mode_forever" msgid="7420011936770086993">"ຈົນກວ່າ​ທ່ານ​ຈະ​ປິດ​"</string>
+    <string name="zen_mode_forever" msgid="7420011936770086993">"ຈົນກວ່າ​ທ່ານ​ຈະ​ປິດ"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"ກຳ​ລັງ​ສາກ​ໄຟ (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> ກວ່າ​ຈ​ະ​ເຕັມ)"</string>
     <string name="guest_nickname" msgid="8059989128963789678">"ແຂກ"</string>
     <string name="guest_new_guest" msgid="4259024453643879653">"+ ແຂກ"</string>
diff --git a/packages/SystemUI/res/values-ms-rMY/strings.xml b/packages/SystemUI/res/values-ms-rMY/strings.xml
index 7d8a1ad..2fed4bd 100644
--- a/packages/SystemUI/res/values-ms-rMY/strings.xml
+++ b/packages/SystemUI/res/values-ms-rMY/strings.xml
@@ -32,16 +32,16 @@
     <string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Tiada pemberitahuan"</string>
     <string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Sedang berlangsung"</string>
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"Pemberitahuan"</string>
-    <string name="battery_low_title" msgid="6456385927409742437">"Bateri rendah"</string>
+    <string name="battery_low_title" msgid="6456385927409742437">"Bateri lemah"</string>
     <string name="battery_low_percent_format" msgid="1077244949318261761">"Berbaki <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
-    <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"<xliff:g id="NUMBER">%d%%</xliff:g> yang tinggal. Penjimat bateri dihidupkan."</string>
+    <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"Tinggal <xliff:g id="NUMBER">%d%%</xliff:g>. Penjimat bateri dihidupkan."</string>
     <string name="invalid_charger" msgid="4549105996740522523">"Pengecasan USB tidak disokong.\nGunakan hanya pengecas yang dibekalkan."</string>
     <string name="invalid_charger_title" msgid="3515740382572798460">"Pengecasan USB tidak disokong."</string>
     <string name="invalid_charger_text" msgid="5474997287953892710">"Gunakan pengecas yang dibekalkan sahaja."</string>
     <string name="battery_low_why" msgid="4553600287639198111">"Tetapan"</string>
-    <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Hidupkan penjimat bateri?"</string>
-    <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Bermula"</string>
-    <string name="battery_saver_start_action" msgid="7245333922937402896">"Hidupkan penjimat bateri"</string>
+    <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Mulakan penjimat bateri?"</string>
+    <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Mula"</string>
+    <string name="battery_saver_start_action" msgid="7245333922937402896">"Mulakan penjimat bateri"</string>
     <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Untuk membantu meningkatkan hayat bateri, penjimat Bateri akan mengurangkan prestasi peranti anda.\n\nPenjimat bateri akan dilumpuhkan apabila peranti anda disambungkan kepada sumber kuasa."</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Tetapan"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
@@ -243,10 +243,8 @@
     <string name="camera_hint" msgid="5241441720959174226">"Leret ke kiri untuk kamera"</string>
     <string name="zen_mode_forever" msgid="7420011936770086993">"Sehingga anda matikan"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Mengecas (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> sehingga penuh)"</string>
-    <!-- no translation found for guest_nickname (8059989128963789678) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (4259024453643879653) -->
-    <skip />
+    <string name="guest_nickname" msgid="8059989128963789678">"Tetamu"</string>
+    <string name="guest_new_guest" msgid="4259024453643879653">"+ Tetamu"</string>
   <plurals name="zen_mode_duration_minutes">
     <item quantity="one" msgid="9040808414992812341">"Selama satu minit"</item>
     <item quantity="other" msgid="6924190729213550991">"Selama %d minit"</item>
diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml
index c099dd9..8ec3621 100644
--- a/packages/SystemUI/res/values-ro/strings.xml
+++ b/packages/SystemUI/res/values-ro/strings.xml
@@ -243,10 +243,8 @@
     <string name="camera_hint" msgid="5241441720959174226">"Glisați la stânga pentru a accesa camera foto"</string>
     <string name="zen_mode_forever" msgid="7420011936770086993">"Până la dezactivare"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"Se încarcă (<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g> până la finalizare)"</string>
-    <!-- no translation found for guest_nickname (8059989128963789678) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (4259024453643879653) -->
-    <skip />
+    <string name="guest_nickname" msgid="8059989128963789678">"Invitat"</string>
+    <string name="guest_new_guest" msgid="4259024453643879653">"+ Invitat"</string>
   <plurals name="zen_mode_duration_minutes">
     <item quantity="one" msgid="9040808414992812341">"Timp de un minut"</item>
     <item quantity="other" msgid="6924190729213550991">"Timp de %d (de) minute"</item>
diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml
index 3bee434..d9c1cfc 100644
--- a/packages/SystemUI/res/values-sr/strings.xml
+++ b/packages/SystemUI/res/values-sr/strings.xml
@@ -42,7 +42,7 @@
     <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Желите ли да покренете Штедњу батерије?"</string>
     <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Покрени"</string>
     <string name="battery_saver_start_action" msgid="7245333922937402896">"Покрените Штедњу батерије"</string>
-    <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Да би продужила век трајања батерије, Штедња батерије ће умањити перформансе уређаја.\n\nШтедња батерије ће се искључити када укључите уређај."</string>
+    <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Да би продужила век трајања батерије, Штедња батерије умањује перформансе уређаја.\n\nШтедња батерије ће се искључити када прикључите уређај на напајање."</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Подешавања"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
     <string name="status_bar_settings_airplane" msgid="4879879698500955300">"Режим рада у авиону"</string>
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 47581a9..9f4c364 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -32,4 +32,7 @@
     <!-- The maximum count of notifications on Keyguard. The rest will be collapsed in an overflow
          card. -->
     <integer name="keyguard_max_notification_count">5</integer>
+
+    <!-- Set to true to enable the user switcher on the keyguard. -->
+    <bool name="config_keyguardUserSwitcher">true</bool>
 </resources>
diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml
index 7635c01..fbd8549f 100644
--- a/packages/SystemUI/res/values-tr/strings.xml
+++ b/packages/SystemUI/res/values-tr/strings.xml
@@ -42,7 +42,7 @@
     <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Pil tasarrufu başlatılsın mı?"</string>
     <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Başlat"</string>
     <string name="battery_saver_start_action" msgid="7245333922937402896">"Pil tasarrufunu başlat"</string>
-    <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Pil tasarrufu, pil ömrünü iyileştirmeye yardımcı olmak için cihazınızın performansını düşürür.\n\nCihazınız fişe takılıyken Pil tasarrufu devre dışı bırakılır."</string>
+    <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Pil tasarrufu, pil ömrünü iyileştirmeye yardımcı olmak için cihazınızın performansını düşürür.\n\nCihazınız fişe takıldığında Pil tasarrufu devre dışı bırakılır."</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Ayarlar"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Kablosuz"</string>
     <string name="status_bar_settings_airplane" msgid="4879879698500955300">"Uçak modu"</string>
diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml
index e15d3e5..5c24e8b 100644
--- a/packages/SystemUI/res/values-vi/strings.xml
+++ b/packages/SystemUI/res/values-vi/strings.xml
@@ -42,7 +42,7 @@
     <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"Khởi động trình tiết kiệm pin?"</string>
     <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"Bắt đầu"</string>
     <string name="battery_saver_start_action" msgid="7245333922937402896">"Khởi động trình tiết kiệm pin"</string>
-    <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Để giúp tăng tuổi thọ pin, trình tiết kiệm pin sẽ giảm hiệu suất của thiết bị.\n\nTrình tiết kiệm pin sẽ tắt khi thiết bị của bạn được cắm vào."</string>
+    <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"Để giúp tăng tuổi thọ pin, trình tiết kiệm pin sẽ giảm hiệu suất của thiết bị.\n\nTrình tiết kiệm pin sẽ tắt khi thiết bị của bạn được cắm vào nguồn điện."</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"Cài đặt"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"Wi-Fi"</string>
     <string name="status_bar_settings_airplane" msgid="4879879698500955300">"Chế độ trên máy bay"</string>
diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml
index cfd4fe9..f975252 100644
--- a/packages/SystemUI/res/values-zh-rCN/strings.xml
+++ b/packages/SystemUI/res/values-zh-rCN/strings.xml
@@ -34,15 +34,15 @@
     <string name="status_bar_latest_events_title" msgid="6594767438577593172">"通知"</string>
     <string name="battery_low_title" msgid="6456385927409742437">"电池电量偏低"</string>
     <string name="battery_low_percent_format" msgid="1077244949318261761">"还剩 <xliff:g id="NUMBER">%d%%</xliff:g>"</string>
-    <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"电量还剩<xliff:g id="NUMBER">%d%%</xliff:g>。节电助手已启用。"</string>
+    <string name="battery_low_percent_format_saver_started" msgid="6534746636002666456">"电量还剩<xliff:g id="NUMBER">%d%%</xliff:g>。节电助手已开启。"</string>
     <string name="invalid_charger" msgid="4549105996740522523">"不支持 USB 充电功能。\n只能使用随附的充电器充电。"</string>
-    <string name="invalid_charger_title" msgid="3515740382572798460">"不支持USB充电功能。"</string>
+    <string name="invalid_charger_title" msgid="3515740382572798460">"不支持USB充电。"</string>
     <string name="invalid_charger_text" msgid="5474997287953892710">"仅限使用设备随附的充电器。"</string>
     <string name="battery_low_why" msgid="4553600287639198111">"设置"</string>
-    <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"启动节电助手?"</string>
-    <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"启动"</string>
-    <string name="battery_saver_start_action" msgid="7245333922937402896">"启动节电助手"</string>
-    <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"为了延长电池的续航时间,节电助手会减降设备的性能。\n\n设备接通电源后,节电助手会自动停用。"</string>
+    <string name="battery_saver_confirmation_title" msgid="5987726159603849352">"要开启节电助手吗?"</string>
+    <string name="battery_saver_confirmation_ok" msgid="7283108887345688413">"开启"</string>
+    <string name="battery_saver_start_action" msgid="7245333922937402896">"开启节电助手"</string>
+    <string name="battery_saver_confirmation_text" msgid="8417584516834617662">"为了延长电池的续航时间,节电助手会减降设备的性能。\n\n设备接通电源后,节电助手会自动关闭。"</string>
     <string name="status_bar_settings_settings_button" msgid="3023889916699270224">"设置"</string>
     <string name="status_bar_settings_wifi_button" msgid="1733928151698311923">"WLAN"</string>
     <string name="status_bar_settings_airplane" msgid="4879879698500955300">"飞行模式"</string>
@@ -245,10 +245,8 @@
     <string name="camera_hint" msgid="5241441720959174226">"向左滑动可打开相机"</string>
     <string name="zen_mode_forever" msgid="7420011936770086993">"直到您将其关闭"</string>
     <string name="keyguard_indication_charging_time" msgid="1757251776872835768">"正在充电(还需<xliff:g id="CHARGING_TIME_LEFT">%s</xliff:g>才能充满)"</string>
-    <!-- no translation found for guest_nickname (8059989128963789678) -->
-    <skip />
-    <!-- no translation found for guest_new_guest (4259024453643879653) -->
-    <skip />
+    <string name="guest_nickname" msgid="8059989128963789678">"访客"</string>
+    <string name="guest_new_guest" msgid="4259024453643879653">"添加新访客"</string>
   <plurals name="zen_mode_duration_minutes">
     <item quantity="one" msgid="9040808414992812341">"1分钟"</item>
     <item quantity="other" msgid="6924190729213550991">"%d分钟"</item>
@@ -257,7 +255,7 @@
     <item quantity="one" msgid="3480040795582254384">"1小时"</item>
     <item quantity="other" msgid="5408537517529822157">"%d小时"</item>
   </plurals>
-    <string name="battery_saver_notification_title" msgid="237918726750955859">"节电助手已启用"</string>
+    <string name="battery_saver_notification_title" msgid="237918726750955859">"节电助手已开启"</string>
     <string name="battery_saver_notification_text" msgid="7796554871101546872">"设备性能已减降。"</string>
     <string name="battery_saver_notification_action_text" msgid="7546297220816993504">"打开节电助手设置"</string>
 </resources>
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index c453618..8473d96 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -54,5 +54,10 @@
         <enum name="horizontal" value="0" />
         <enum name="vertical" value="1" />
     </attr>
+    <declare-styleable name="UserAvatarView">
+        <attr name="frameWidth" format="dimension" />
+        <attr name="activeFrameColor" format="color" />
+        <attr name="frameColor" />
+    </declare-styleable>
 </resources>
 
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8c1a9c7..4e38da6 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -46,9 +46,7 @@
     <color name="keyguard_overflow_content_color">#ff686868</color>
 
     <!-- The default recents task bar background color. -->
-    <color name="recents_task_bar_default_background_color">#e6444444</color>
-    <!-- The default recents task bar text color. -->
-    <color name="recents_task_bar_default_text_color">#ffeeeeee</color>
+    <color name="recents_task_bar_default_background_color">#ffe6e6e6</color>
     <!-- The recents task bar light text color to be drawn on top of dark backgrounds. -->
     <color name="recents_task_bar_light_text_color">#ffeeeeee</color>
     <!-- The recents task bar dark text color to be drawn on top of light backgrounds. -->
@@ -62,6 +60,9 @@
 
     <color name="keyguard_affordance">#ffffffff</color>
 
+    <!-- The color of the circle around the primary user in the user switcher -->
+    <color name="current_user_border_color">@color/primary_color</color>
+
     <!-- Our material color palette (deep teal) -->
     <color name="primary_color">#ff7fcac3</color>
     <color name="background_color_1">#ff384248</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index c8851dc..c1f971a 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -109,11 +109,11 @@
     <integer name="heads_up_sensitivity_delay">700</integer>
 
     <!-- The duration in seconds to wait before the dismiss buttons are shown. -->
-    <integer name="recents_task_bar_dismiss_delay_seconds">3</integer>
+    <integer name="recents_task_bar_dismiss_delay_seconds">1</integer>
     <!-- The min animation duration for animating views that are currently visible. -->
-    <integer name="recents_filter_animate_current_views_min_duration">175</integer>
+    <integer name="recents_filter_animate_current_views_duration">250</integer>
     <!-- The min animation duration for animating views that are newly visible. -->
-    <integer name="recents_filter_animate_new_views_min_duration">125</integer>
+    <integer name="recents_filter_animate_new_views_duration">250</integer>
     <!-- The min animation duration for animating the task bar in. -->
     <integer name="recents_animate_task_bar_enter_duration">275</integer>
     <!-- The animation delay for animating the first task in. This should roughly be the animation
@@ -154,5 +154,8 @@
          Notification.tickerText across the status bar for what seems like an
          eternity. -->
     <bool name="enable_ticker">false</bool>
+
+    <!-- Set to true to enable the user switcher on the keyguard. -->
+    <bool name="config_keyguardUserSwitcher">false</bool>
 </resources>
 
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 8b8c126..36c1994 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -212,7 +212,7 @@
     <dimen name="glowpadview_inner_radius">15dip</dimen>
 
     <!-- The size of the application icon in the recents task view. -->
-    <dimen name="recents_task_view_application_icon_size">32dp</dimen>
+    <dimen name="recents_task_view_application_icon_size">48dp</dimen>
 
     <!-- The size of the activity icon in the recents task view. -->
     <dimen name="recents_task_view_activity_icon_size">60dp</dimen>
@@ -320,6 +320,9 @@
          device. -->
     <dimen name="unlock_move_distance">75dp</dimen>
 
+    <!-- Distance after which the scrim starts fading in when dragging down the quick settings -->
+    <dimen name="notification_scrim_wait_distance">100dp</dimen>
+
     <!-- Move distance for the unlock hint animation on the lockscreen -->
     <dimen name="hint_move_distance">75dp</dimen>
 
@@ -332,4 +335,11 @@
 
     <!-- end margin for multi user switch in expanded quick settings -->
     <dimen name="multi_user_switch_expanded_margin">8dp</dimen>
+
+    <!-- end margin for system icons if multi user switch is hidden -->
+    <dimen name="system_icons_switcher_hidden_expanded_margin">16dp</dimen>
+
+    <!-- The thickness of the colored border around the current user. -->
+    <dimen name="keyguard_user_switcher_border_thickness">2dp</dimen>
+
 </resources>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index e5499ee..f021253 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -577,14 +577,6 @@
     <!-- Description of the left direction in which one can to slide the handle in the Slide unlock screen. [CHAR LIMIT=NONE] -->
     <string name="description_direction_left">"Slide left for <xliff:g id="target_description" example="Unlock">%s</xliff:g>.</string>
 
-    <!-- Zen mode: Summary notification content title. [CHAR LIMIT=NONE] -->
-    <plurals name="zen_mode_notification_title">
-        <item quantity="one">Notification hidden</item>
-        <item quantity="other">%d notifications hidden</item>
-    </plurals>
-    <!-- Zen mode: Summary notification content text. [CHAR LIMIT=NONE] -->
-    <string name="zen_mode_notification_text">Touch to show</string>
-
     <!-- Zen mode: Short title. [CHAR LIMIT=40] -->
     <string name="zen_mode_title">Do not disturb</string>
 
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index c117eba..e5d5b03 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -100,6 +100,13 @@
     <style name="TextAppearance.StatusBar.Expanded.Network.EmergencyOnly">
     </style>
 
+    <style name="TextAppearance.StatusBar.Expanded.UserSwitcher">
+        <item name="android:textSize">16sp</item>
+        <item name="android:textStyle">normal</item>
+        <item name="android:textColor">#ffffff</item>
+    </style>
+    <style name="TextAppearance.StatusBar.Expanded.UserSwitcher.UserName" />
+
     <style name="TextAppearance" />
     <style name="TextAppearance.QuickSettings" />
 
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index ffd76a7..b9e2e1b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -239,6 +239,12 @@
     private String mPhoneState = TelephonyManager.EXTRA_STATE_IDLE;
 
     /**
+     * Whether a hide is pending an we are just waiting for #startKeyguardExitAnimation to be
+     * called.
+     * */
+    private boolean mHiding;
+
+    /**
      * we send this intent when the keyguard is dismissed.
      */
     private static final Intent USER_PRESENT_INTENT = new Intent(Intent.ACTION_USER_PRESENT)
@@ -1169,6 +1175,7 @@
             }
 
             mStatusBarKeyguardViewManager.show(options);
+            mHiding = false;
             mShowing = true;
             mKeyguardDonePending = false;
             updateActivityLockScreenState();
@@ -1191,7 +1198,7 @@
         synchronized (KeyguardViewMediator.this) {
             if (DEBUG) Log.d(TAG, "handleHide");
             try {
-
+                mHiding = true;
                 if (mShowing) {
 
                     // Don't actually hide the Keyguard at the moment, wait for window manager until
@@ -1212,6 +1219,11 @@
     private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) {
         synchronized (KeyguardViewMediator.this) {
 
+            if (!mHiding) {
+                return;
+            }
+            mHiding = false;
+
             // only play "unlock" noises if not on a call (since the incall UI
             // disables the keyguard)
             if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) {
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index c3ba349..41b1f75 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -152,6 +152,7 @@
                 .setContentText(mContext.getString(R.string.invalid_charger_text))
                 .setPriority(Notification.PRIORITY_MAX)
                 .setCategory(Notification.CATEGORY_SYSTEM)
+                .setVisibility(Notification.VISIBILITY_PUBLIC)
                 .setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_CHARGER), true);
         final Notification n = nb.build();
         if (n.headsUpContentView != null) {
@@ -171,6 +172,7 @@
                 .setOngoing(true)
                 .setPriority(Notification.PRIORITY_MAX)
                 .setCategory(Notification.CATEGORY_SYSTEM)
+                .setVisibility(Notification.VISIBILITY_PUBLIC)
                 .setFullScreenIntent(pendingBroadcast(ACTION_SHOW_FALLBACK_WARNING), true);
         if (hasBatterySettings()) {
             nb.setContentIntent(pendingBroadcast(ACTION_SHOW_BATTERY_SETTINGS));
@@ -197,7 +199,8 @@
                 .setContentText(mContext.getString(R.string.battery_saver_notification_text))
                 .setOngoing(true)
                 .setShowWhen(false)
-                .setCategory(Notification.CATEGORY_SYSTEM);
+                .setCategory(Notification.CATEGORY_SYSTEM)
+                .setVisibility(Notification.VISIBILITY_PUBLIC);
         if (hasSaverSettings()) {
             nb.addAction(0,
                     mContext.getString(R.string.battery_saver_notification_action_text),
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Constants.java b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
index 1d355cd..ddea0bf 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Constants.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Constants.java
@@ -30,7 +30,7 @@
             // Enables the screenshot app->Recents transition
             public static final boolean EnableScreenshotAppTransition = false;
             // Enables the filtering of tasks according to their grouping
-            public static final boolean EnableTaskFiltering = false;
+            public static final boolean EnableTaskFiltering = true;
             // Enables clipping of tasks against each other
             public static final boolean EnableTaskStackClipping = true;
             // Enables the use of theme colors as the task bar background
@@ -48,7 +48,7 @@
             // For debugging, this defines the number of mock recents packages to create
             public static final int SystemServicesProxyMockPackageCount = 3;
             // For debugging, this defines the number of mock recents tasks to create
-            public static final int SystemServicesProxyMockTaskCount = 75;
+            public static final int SystemServicesProxyMockTaskCount = 100;
         }
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
index 247aa6f..f0b2cb6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/DozeTrigger.java
@@ -49,12 +49,25 @@
 
     /** Starts dozing. This also resets the trigger flag. */
     public void startDozing() {
-        poke();
+        forcePoke();
         mHasTriggered = false;
     }
 
-    /** Poke this dozer to wake it up for a little bit. */
+    /** Stops dozing. */
+    public void stopDozing() {
+        mHandler.removeCallbacks(mDozeRunnable);
+        mIsDozing = false;
+    }
+
+    /** Poke this dozer to wake it up for a little bit, if it is dozing. */
     public void poke() {
+        if (mIsDozing) {
+            forcePoke();
+        }
+    }
+
+    /** Poke this dozer to wake it up for a little bit. */
+    void forcePoke() {
         mHandler.removeCallbacks(mDozeRunnable);
         mHandler.postDelayed(mDozeRunnable, mDozeDurationSeconds * 1000);
         mIsDozing = true;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index dce8f57..21704a4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -17,7 +17,7 @@
 package com.android.systemui.recents;
 
 import android.app.Activity;
-import android.appwidget.AppWidgetHost;
+import android.app.ActivityOptions;
 import android.appwidget.AppWidgetHostView;
 import android.appwidget.AppWidgetManager;
 import android.appwidget.AppWidgetProviderInfo;
@@ -26,8 +26,8 @@
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.Bundle;
+import android.os.UserHandle;
 import android.util.Pair;
-import android.view.Gravity;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -38,32 +38,13 @@
 import com.android.systemui.recents.model.TaskStack;
 import com.android.systemui.recents.views.FullScreenTransitionView;
 import com.android.systemui.recents.views.RecentsView;
+import com.android.systemui.recents.views.SystemBarScrimViews;
 import com.android.systemui.recents.views.ViewAnimation;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.ArrayList;
 
-/** Our special app widget host */
-class RecentsAppWidgetHost extends AppWidgetHost {
-    /* Callbacks to notify when an app package changes */
-    interface RecentsAppWidgetHostCallbacks {
-        public void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo);
-    }
-
-    RecentsAppWidgetHostCallbacks mCb;
-
-    public RecentsAppWidgetHost(Context context, int hostId, RecentsAppWidgetHostCallbacks cb) {
-        super(context, hostId);
-        mCb = cb;
-    }
-
-    @Override
-    protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
-        mCb.onProviderChanged(appWidgetId, appWidget);
-    }
-}
-
 /* Activity */
 public class RecentsActivity extends Activity implements RecentsView.RecentsViewCallbacks,
         RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks,
@@ -71,29 +52,61 @@
 
     FrameLayout mContainerView;
     RecentsView mRecentsView;
+    SystemBarScrimViews mScrimViews;
     View mEmptyView;
-    View mNavBarScrimView;
     FullScreenTransitionView mFullScreenshotView;
 
     RecentsConfiguration mConfig;
 
-    AppWidgetHost mAppWidgetHost;
+    RecentsAppWidgetHost mAppWidgetHost;
     AppWidgetProviderInfo mSearchAppWidgetInfo;
     AppWidgetHostView mSearchAppWidgetHostView;
 
     boolean mVisible;
     boolean mTaskLaunched;
 
-    private static Method sPropertyMethod;
-    static {
-        try {
-            Class<?> c = Class.forName("android.view.GLES20Canvas");
-            sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class);
-            if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true);
-        } catch (ClassNotFoundException e) {
-            e.printStackTrace();
-        } catch (NoSuchMethodException e) {
-            e.printStackTrace();
+    // Runnables to finish the Recents activity
+    FinishRecentsRunnable mFinishRunnable = new FinishRecentsRunnable(true);
+    FinishRecentsRunnable mFinishWithoutAnimationRunnable = new FinishRecentsRunnable(false);
+    FinishRecentsRunnable mFinishLaunchHomeRunnable;
+
+    /**
+     * A Runnable to finish Recents either with/without a transition, and either by calling finish()
+     * or just launching the specified intent.
+     */
+    class FinishRecentsRunnable implements Runnable {
+        boolean mUseCustomFinishTransition;
+        Intent mLaunchIntent;
+        ActivityOptions mLaunchOpts;
+
+        public FinishRecentsRunnable(boolean withTransition) {
+            mUseCustomFinishTransition = withTransition;
+        }
+
+        public FinishRecentsRunnable(Intent launchIntent, ActivityOptions opts) {
+            mLaunchIntent = launchIntent;
+            mLaunchOpts = opts;
+        }
+
+        @Override
+        public void run() {
+            // Mark Recents as no longer visible
+            AlternateRecentsComponent.notifyVisibilityChanged(false);
+            // Finish Recents
+            if (mLaunchIntent != null) {
+                if (mLaunchOpts != null) {
+                    startActivityAsUser(mLaunchIntent, new UserHandle(UserHandle.USER_CURRENT));
+                } else {
+                    startActivityAsUser(mLaunchIntent, mLaunchOpts.toBundle(),
+                            new UserHandle(UserHandle.USER_CURRENT));
+                }
+            } else {
+                finish();
+                if (mUseCustomFinishTransition) {
+                    overridePendingTransition(R.anim.recents_to_launcher_enter,
+                            R.anim.recents_to_launcher_exit);
+                }
+            }
         }
     }
 
@@ -114,9 +127,9 @@
                     // If we are mid-animation into Recents, then reverse it and finish
                     if (mFullScreenshotView == null ||
                             !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
-                        // Otherwise, just finish the activity without launching any other activities
+                        // Otherwise, either finish Recents, or launch Home directly
                         ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(context,
-                                null, mFinishRunnable, null);
+                                null, mFinishLaunchHomeRunnable, null);
                         mRecentsView.startExitToHomeAnimation(
                                 new ViewAnimation.TaskViewExitContext(exitTrigger));
                     }
@@ -140,23 +153,7 @@
     BroadcastReceiver mScreenOffReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // Mark recents as no longer visible
-            AlternateRecentsComponent.notifyVisibilityChanged(false);
-            // Finish without an animations
-            finish();
-        }
-    };
-
-    // A runnable to finish the Recents activity
-    Runnable mFinishRunnable = new Runnable() {
-        @Override
-        public void run() {
-            // Mark recents as no longer visible
-            AlternateRecentsComponent.notifyVisibilityChanged(false);
-            // Finish with an animations
-            finish();
-            overridePendingTransition(R.anim.recents_to_launcher_enter,
-                    R.anim.recents_to_launcher_exit);
+            mFinishWithoutAnimationRunnable.run();
         }
     };
 
@@ -180,13 +177,8 @@
                 AlternateRecentsComponent.EXTRA_TRIGGERED_FROM_ALT_TAB, false);
         mConfig.launchedWithNoRecentTasks = !root.hasTasks();
 
-        if (mConfig.shouldAnimateNavBarScrim()) {
-            // Hide the scrim if we animate into Recents with window transitions
-            mNavBarScrimView.setVisibility(View.INVISIBLE);
-        } else {
-            // Show the scrim if we animate into Recents without window transitions
-            mNavBarScrimView.setVisibility(View.VISIBLE);
-        }
+        // Show the scrim if we animate into Recents without window transitions
+        mScrimViews.prepareEnterRecentsAnimation();
 
         // Add the default no-recents layout
         if (mConfig.launchedWithNoRecentTasks) {
@@ -278,13 +270,18 @@
                     !mFullScreenshotView.cancelAnimateOnEnterRecents(mFinishRunnable)) {
                 // If we have a focused task, then launch that task
                 if (!mRecentsView.launchFocusedTask()) {
-                    // If there are any tasks, then launch the first task
-                    if (!mRecentsView.launchFirstTask()) {
-                        // We really shouldn't hit this, but if we do, just animate out (aka. finish)
+                    if (mConfig.launchedFromHome) {
+                        // Just start the animation out of recents
                         ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
-                                null, mFinishRunnable, null);
+                                null, mFinishLaunchHomeRunnable, null);
                         mRecentsView.startExitToHomeAnimation(
                                 new ViewAnimation.TaskViewExitContext(exitTrigger));
+                    } else {
+                        // Otherwise, try and launch the first task
+                        if (!mRecentsView.launchFirstTask()) {
+                            // If there are no tasks, then just finish recents
+                            mFinishLaunchHomeRunnable.run();
+                        }
                     }
                 }
             }
@@ -309,8 +306,17 @@
         RecentsTaskLoader.initialize(this);
         mConfig = RecentsConfiguration.reinitialize(this);
 
+        // Create the home intent runnable
+        Intent homeIntent = new Intent(Intent.ACTION_MAIN, null);
+        homeIntent.addCategory(Intent.CATEGORY_HOME);
+        homeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
+                            Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+        mFinishLaunchHomeRunnable = new FinishRecentsRunnable(homeIntent,
+                ActivityOptions.makeCustomAnimation(this, R.anim.recents_to_launcher_enter,
+                        R.anim.recents_to_launcher_exit));
+
         // Initialize the widget host (the host id is static and does not change)
-        mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId, this);
+        mAppWidgetHost = new RecentsAppWidgetHost(this, Constants.Values.App.AppWidgetHostId);
 
         // Create the view hierarchy
         mRecentsView = new RecentsView(this);
@@ -325,23 +331,23 @@
         // Create the empty view
         LayoutInflater inflater = LayoutInflater.from(this);
         mEmptyView = inflater.inflate(R.layout.recents_empty, mContainerView, false);
-        mNavBarScrimView = inflater.inflate(R.layout.recents_nav_bar_scrim, mContainerView, false);
-        mNavBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
-                ViewGroup.LayoutParams.MATCH_PARENT,
-                ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM));
+        mScrimViews = new SystemBarScrimViews(mConfig);
+        mScrimViews.inflate(inflater, mContainerView);
         if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
             mFullScreenshotView = new FullScreenTransitionView(this, this);
             mFullScreenshotView.setLayoutParams(new FrameLayout.LayoutParams(
                     ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
         }
 
+        // Add the views to the layout
         mContainerView = new FrameLayout(this);
+        mContainerView.addView(mScrimViews.getStatusBarScrimView());
         mContainerView.addView(mRecentsView);
         mContainerView.addView(mEmptyView);
         if (Constants.DebugFlags.App.EnableScreenshotAppTransition) {
             mContainerView.addView(mFullScreenshotView);
         }
-        mContainerView.addView(mNavBarScrimView);
+        mContainerView.addView(mScrimViews.getNavBarScrimView());
         setContentView(mContainerView);
 
         // Update the recent tasks
@@ -362,10 +368,10 @@
             onConfigurationChange();
         }
 
-        // XXX: Update the shadows
+        // Private API calls to make the shadows look better
         try {
-            sPropertyMethod.invoke(null, "ambientShadowStrength", String.valueOf(35f));
-            sPropertyMethod.invoke(null, "ambientRatio", String.valueOf(0.5f));
+            Utilities.setShadowProperty("ambientShadowStrength", String.valueOf(35f));
+            Utilities.setShadowProperty("ambientRatio", String.valueOf(0.5f));
         } catch (IllegalAccessException e) {
             e.printStackTrace();
         } catch (InvocationTargetException e) {
@@ -418,11 +424,6 @@
         }
         super.onStart();
 
-        // Start listening for widget package changes if there is one bound
-        if (mConfig.searchBarAppWidgetId >= 0) {
-            mAppWidgetHost.startListening();
-        }
-
         mVisible = true;
     }
 
@@ -458,6 +459,11 @@
 
         // Register any broadcast receivers for the task loader
         RecentsTaskLoader.getInstance().registerReceivers(this, mRecentsView);
+
+        // Start listening for widget package changes if there is one bound
+        if (mConfig.searchBarAppWidgetId >= 0) {
+            mAppWidgetHost.startListening(this);
+        }
     }
 
     @Override
@@ -473,6 +479,11 @@
         unregisterReceiver(mServiceBroadcastReceiver);
         unregisterReceiver(mScreenOffReceiver);
         RecentsTaskLoader.getInstance().unregisterReceivers();
+
+        // Stop listening for widget package changes if there was one bound
+        if (mConfig.searchBarAppWidgetId >= 0) {
+            mAppWidgetHost.stopListening();
+        }
     }
 
     @Override
@@ -492,11 +503,6 @@
         }
         super.onStop();
 
-        // Stop listening for widget package changes if there was one bound
-        if (mConfig.searchBarAppWidgetId >= 0) {
-            mAppWidgetHost.stopListening();
-        }
-
         mVisible = false;
         mTaskLaunched = false;
     }
@@ -545,17 +551,14 @@
                 if (mConfig.launchedFromHome) {
                     // Just start the animation out of recents
                     ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
-                            null, mFinishRunnable, null);
+                            null, mFinishLaunchHomeRunnable, null);
                     mRecentsView.startExitToHomeAnimation(
                             new ViewAnimation.TaskViewExitContext(exitTrigger));
                 } else {
                     // Otherwise, try and launch the first task
                     if (!mRecentsView.launchFirstTask()) {
                         // If there are no tasks, then just finish recents
-                        ReferenceCountedTrigger exitTrigger = new ReferenceCountedTrigger(this,
-                                null, mFinishRunnable, null);
-                        mRecentsView.startExitToHomeAnimation(
-                                new ViewAnimation.TaskViewExitContext(exitTrigger));
+                        mFinishLaunchHomeRunnable.run();
                     }
                 }
             }
@@ -563,31 +566,11 @@
     }
 
     public void onEnterAnimationTriggered() {
-        // Fade in the scrim
-        if (mConfig.shouldAnimateNavBarScrim() && mConfig.hasNavBarScrim()) {
-            mNavBarScrimView.setVisibility(View.VISIBLE);
-            mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
-            mNavBarScrimView.animate()
-                    .translationY(0)
-                    .setStartDelay(mConfig.taskBarEnterAnimDelay)
-                    .setDuration(mConfig.navBarScrimEnterDuration)
-                    .setInterpolator(mConfig.quintOutInterpolator)
-                    .start();
-        }
+        // Animate the scrims in
+        mScrimViews.startEnterRecentsAnimation();
     }
 
-    @Override
-    public void onExitAnimationTriggered() {
-        // Fade out the scrim
-        if (mConfig.shouldAnimateNavBarScrim() && mConfig.hasNavBarScrim()) {
-            mNavBarScrimView.animate()
-                    .translationY(mNavBarScrimView.getMeasuredHeight())
-                    .setStartDelay(0)
-                    .setDuration(mConfig.taskBarExitAnimDuration)
-                    .setInterpolator(mConfig.fastOutSlowInInterpolator)
-                    .start();
-        }
-    }
+    /**** FullScreenTransitionView.FullScreenTransitionViewCallbacks Implementation ****/
 
     @Override
     public void onEnterAnimationComplete(boolean canceled) {
@@ -604,29 +587,28 @@
         AlternateRecentsComponent.consumeLastScreenshot();
     }
 
-    @Override
-    public void onTaskLaunching(boolean isTaskInStackBounds) {
-        mTaskLaunched = true;
+    /**** RecentsView.RecentsViewCallbacks Implementation ****/
 
-        // Fade out the scrim
-        if (!isTaskInStackBounds && mConfig.hasNavBarScrim()) {
-            onExitAnimationTriggered();
-        }
+    @Override
+    public void onExitToHomeAnimationTriggered() {
+        // Animate the scrims out
+        mScrimViews.startExitRecentsAnimation();
+    }
+
+    @Override
+    public void onTaskLaunching() {
+        mTaskLaunched = true;
 
         // Mark recents as no longer visible
         AlternateRecentsComponent.notifyVisibilityChanged(false);
     }
 
+    /**** RecentsAppWidgetHost.RecentsAppWidgetHostCallbacks Implementation ****/
+
     @Override
-    public void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
-        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
-        if (appWidgetId > -1 && appWidgetId == mConfig.searchBarAppWidgetId) {
-            // The search provider may have changed, so just delete the old widget and bind it again
-            ssp.unbindSearchAppWidget(mAppWidgetHost, appWidgetId);
-            mConfig.updateSearchBarAppWidgetId(this, -1);
-            // Load the widget again
-            bindSearchBarAppWidget();
-            addSearchBarAppWidgetView();
-        }
+    public void refreshSearchWidget() {
+        // Load the Search widget again
+        bindSearchBarAppWidget();
+        addSearchBarAppWidgetView();
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
new file mode 100644
index 0000000..d55eba7
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsAppWidgetHost.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.Context;
+
+/** Our special app widget host for the Search widget */
+public class RecentsAppWidgetHost extends AppWidgetHost {
+
+    /* Callbacks to notify when an app package changes */
+    interface RecentsAppWidgetHostCallbacks {
+        public void refreshSearchWidget();
+    }
+
+    Context mContext;
+    RecentsAppWidgetHostCallbacks mCb;
+    RecentsConfiguration mConfig;
+
+    public RecentsAppWidgetHost(Context context, int hostId) {
+        super(context, hostId);
+        mContext = context;
+        mConfig = RecentsConfiguration.getInstance();
+    }
+
+    public void startListening(RecentsAppWidgetHostCallbacks cb) {
+        mCb = cb;
+        super.startListening();
+    }
+
+    @Override
+    public void stopListening() {
+        super.stopListening();
+        // Ensure that we release any references to the callbacks
+        mCb = null;
+        mContext = null;
+    }
+
+    @Override
+    protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidgetInfo) {
+        if (mCb == null) return;
+
+        SystemServicesProxy ssp = RecentsTaskLoader.getInstance().getSystemServicesProxy();
+        if (appWidgetId > -1 && appWidgetId == mConfig.searchBarAppWidgetId) {
+            // The search provider may have changed, so just delete the old widget and bind it again
+            ssp.unbindSearchAppWidget(this, appWidgetId);
+            // Update the search widget
+            mConfig.updateSearchBarAppWidgetId(mContext, -1);
+            mCb.refreshSearchWidget();
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index c1a8ee6..10978ca 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -47,8 +47,8 @@
     public Interpolator quintOutInterpolator;
 
     /** Filtering */
-    public int filteringCurrentViewsMinAnimDuration;
-    public int filteringNewViewsMinAnimDuration;
+    public int filteringCurrentViewsAnimDuration;
+    public int filteringNewViewsAnimDuration;
 
     /** Insets */
     public Rect systemInsets = new Rect();
@@ -81,7 +81,6 @@
 
     /** Task bar colors */
     public int taskBarViewDefaultBackgroundColor;
-    public int taskBarViewDefaultTextColor;
     public int taskBarViewLightTextColor;
     public int taskBarViewDarkTextColor;
     public int taskBarViewHighlightColor;
@@ -106,12 +105,30 @@
     public boolean developerOptionsEnabled;
 
     /** Private constructor */
-    private RecentsConfiguration() {}
+    private RecentsConfiguration(Context context) {
+        // Properties that don't have to be reloaded with each configuration change can be loaded
+        // here.
+
+        // Interpolators
+        fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_slow_in);
+        fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.fast_out_linear_in);
+        linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.linear_out_slow_in);
+        quintOutInterpolator = AnimationUtils.loadInterpolator(context,
+                com.android.internal.R.interpolator.decelerate_quint);
+
+        // Check if the developer options are enabled
+        ContentResolver cr = context.getContentResolver();
+        developerOptionsEnabled = Settings.Global.getInt(cr,
+                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
+    }
 
     /** Updates the configuration to the current context */
     public static RecentsConfiguration reinitialize(Context context) {
         if (sInstance == null) {
-            sInstance = new RecentsConfiguration();
+            sInstance = new RecentsConfiguration(context);
         }
         sInstance.update(context);
         return sInstance;
@@ -132,21 +149,11 @@
         animationPxMovementPerSecond =
                 res.getDimensionPixelSize(R.dimen.recents_animation_movement_in_dps_per_second);
 
-        // Interpolators
-        fastOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_slow_in);
-        fastOutLinearInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.fast_out_linear_in);
-        linearOutSlowInInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.linear_out_slow_in);
-        quintOutInterpolator = AnimationUtils.loadInterpolator(context,
-                com.android.internal.R.interpolator.decelerate_quint);
-
         // Filtering
-        filteringCurrentViewsMinAnimDuration =
-                res.getInteger(R.integer.recents_filter_animate_current_views_min_duration);
-        filteringNewViewsMinAnimDuration =
-                res.getInteger(R.integer.recents_filter_animate_new_views_min_duration);
+        filteringCurrentViewsAnimDuration =
+                res.getInteger(R.integer.recents_filter_animate_current_views_duration);
+        filteringNewViewsAnimDuration =
+                res.getInteger(R.integer.recents_filter_animate_new_views_duration);
 
         // Insets
         displayRect.set(0, 0, dm.widthPixels, dm.heightPixels);
@@ -194,8 +201,6 @@
         // Task bar colors
         taskBarViewDefaultBackgroundColor =
                 res.getColor(R.color.recents_task_bar_default_background_color);
-        taskBarViewDefaultTextColor =
-                res.getColor(R.color.recents_task_bar_default_text_color);
         taskBarViewLightTextColor =
                 res.getColor(R.color.recents_task_bar_light_text_color);
         taskBarViewDarkTextColor =
@@ -217,11 +222,6 @@
         navBarScrimEnterDuration =
                 res.getInteger(R.integer.recents_nav_bar_scrim_enter_duration);
 
-        // Check if the developer options are enabled
-        ContentResolver cr = context.getContentResolver();
-        developerOptionsEnabled = Settings.Global.getInt(cr,
-                Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0;
-
         if (Console.Enabled) {
             Console.log(Constants.Log.UI.MeasureAndLayout,
                     "[RecentsConfiguration|orientation]", isLandscape ? "Landscape" : "Portrait",
@@ -257,6 +257,16 @@
         return searchBarAppWidgetId >= 0;
     }
 
+    /** Returns whether the status bar scrim should be animated when shown for the first time. */
+    public boolean shouldAnimateStatusBarScrim() {
+        return launchedFromHome;
+    }
+
+    /** Returns whether the status bar scrim should be visible. */
+    public boolean hasStatusBarScrim() {
+        return !launchedWithNoRecentTasks;
+    }
+
     /** Returns whether the nav bar scrim should be animated when shown for the first time. */
     public boolean shouldAnimateNavBarScrim() {
         return true;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java
index 8bcc7f5..04d1f1f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsPackageMonitor.java
@@ -37,29 +37,29 @@
 
     PackageCallbacks mCb;
     List<ActivityManager.RecentTaskInfo> mTasks;
-    SystemServicesProxy mSsp;
-    boolean mRegistered;
-
-    public RecentsPackageMonitor(Context context) {
-        mSsp = new SystemServicesProxy(context);
-    }
+    SystemServicesProxy mSystemServicesProxy;
 
     /** Registers the broadcast receivers with the specified callbacks. */
     public void register(Context context, PackageCallbacks cb) {
+        mSystemServicesProxy = new SystemServicesProxy(context);
         mCb = cb;
-        if (!mRegistered) {
+        try {
             register(context, Looper.getMainLooper(), false);
-            mRegistered = true;
+        } catch (IllegalStateException e) {
+            e.printStackTrace();
         }
     }
 
     /** Unregisters the broadcast receivers. */
     @Override
     public void unregister() {
-        if (mRegistered) {
+        try {
             super.unregister();
-            mRegistered = false;
+        } catch (IllegalStateException e) {
+            e.printStackTrace();
         }
+        mSystemServicesProxy = null;
+        mCb = null;
         mTasks.clear();
     }
 
@@ -106,7 +106,7 @@
                     // If we know that the component still exists in the package, then skip
                     continue;
                 }
-                if (mSsp.getActivityInfo(cn) != null) {
+                if (mSystemServicesProxy.getActivityInfo(cn) != null) {
                     componentsKnownToExist.add(cn);
                 } else {
                     componentsToRemove.add(cn);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
index a02e1a7b..07a6a56 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsTaskLoader.java
@@ -118,6 +118,7 @@
     TaskResourceLoadQueue mLoadQueue;
     DrawableLruCache mApplicationIconCache;
     BitmapLruCache mThumbnailCache;
+    Bitmap mDefaultThumbnail;
 
     boolean mCancelled;
     boolean mWaitingOnLoadQueue;
@@ -125,10 +126,12 @@
     /** Constructor, creates a new loading thread that loads task resources in the background */
     public TaskResourceLoader(TaskResourceLoadQueue loadQueue,
                               DrawableLruCache applicationIconCache,
-                              BitmapLruCache thumbnailCache) {
+                              BitmapLruCache thumbnailCache,
+                              Bitmap defaultThumbnail) {
         mLoadQueue = loadQueue;
         mApplicationIconCache = applicationIconCache;
         mThumbnailCache = thumbnailCache;
+        mDefaultThumbnail = defaultThumbnail;
         mMainThreadHandler = new Handler();
         mLoadThread = new HandlerThread("Recents-TaskResourceLoader");
         mLoadThread.setPriority(Thread.NORM_PRIORITY - 1);
@@ -238,6 +241,7 @@
                                 loadThumbnail = thumbnail;
                                 mThumbnailCache.put(t.key, thumbnail);
                             } else {
+                                loadThumbnail = mDefaultThumbnail;
                                 Console.logError(mContext,
                                         "Failed to load task top thumbnail for: " +
                                                 t.key.baseIntent.getComponent().getPackageName());
@@ -330,6 +334,7 @@
 
     BitmapDrawable mDefaultApplicationIcon;
     Bitmap mDefaultThumbnail;
+    Bitmap mLoadingThumbnail;
 
     /** Private Constructor */
     private RecentsTaskLoader(Context context) {
@@ -352,22 +357,26 @@
 
         // Initialize the proxy, cache and loaders
         mSystemServicesProxy = new SystemServicesProxy(context);
-        mPackageMonitor = new RecentsPackageMonitor(context);
+        mPackageMonitor = new RecentsPackageMonitor();
         mLoadQueue = new TaskResourceLoadQueue();
         mApplicationIconCache = new DrawableLruCache(iconCacheSize);
         mThumbnailCache = new BitmapLruCache(thumbnailCacheSize);
-        mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache);
+        mLoader = new TaskResourceLoader(mLoadQueue, mApplicationIconCache, mThumbnailCache,
+                mDefaultThumbnail);
 
         // Create the default assets
         Bitmap icon = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
         icon.eraseColor(0x00000000);
         mDefaultThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
-        mDefaultThumbnail.eraseColor(0x00000000);
+        mDefaultThumbnail.eraseColor(0xFFffffff);
+        mLoadingThumbnail = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+        mLoadingThumbnail.eraseColor(0x00000000);
         mDefaultApplicationIcon = new BitmapDrawable(context.getResources(), icon);
         if (Console.Enabled) {
             Console.log(Constants.Log.App.TaskDataLoader,
                     "[RecentsTaskLoader|defaultBitmaps]",
-                    "icon: " + mDefaultApplicationIcon + " thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
+                    "icon: " + mDefaultApplicationIcon +
+                    " default thumbnail: " + mDefaultThumbnail, Console.AnsiRed);
         }
     }
 
@@ -394,7 +403,7 @@
 
         SystemServicesProxy ssp = mSystemServicesProxy;
         List<ActivityManager.RecentTaskInfo> tasks =
-                ssp.getRecentTasks(100, UserHandle.CURRENT.getIdentifier());
+                ssp.getRecentTasks(50, UserHandle.CURRENT.getIdentifier());
         Collections.reverse(tasks);
         if (Console.Enabled) {
             Console.log(Constants.Log.App.TimeSystemCalls,
@@ -544,7 +553,7 @@
             requiresLoad = true;
         }
         if (thumbnail == null) {
-            thumbnail = mDefaultThumbnail;
+            thumbnail = mLoadingThumbnail;
             requiresLoad = true;
         }
         if (requiresLoad) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java b/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
index 2f89e6d2..d525546 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ReferenceCountedTrigger.java
@@ -18,6 +18,8 @@
 
 import android.content.Context;
 
+import java.util.ArrayList;
+
 /**
  * A ref counted trigger that does some logic when the count is first incremented, or last
  * decremented.  Not thread safe as it's not currently needed.
@@ -26,8 +28,8 @@
 
     Context mContext;
     int mCount;
-    Runnable mFirstIncRunnable;
-    Runnable mLastDecRunnable;
+    ArrayList<Runnable> mFirstIncRunnables = new ArrayList<Runnable>();
+    ArrayList<Runnable> mLastDecRunnables = new ArrayList<Runnable>();
     Runnable mErrorRunnable;
 
     // Convenience runnables
@@ -47,15 +49,18 @@
     public ReferenceCountedTrigger(Context context, Runnable firstIncRunnable,
                                    Runnable lastDecRunnable, Runnable errorRunanable) {
         mContext = context;
-        mFirstIncRunnable = firstIncRunnable;
-        mLastDecRunnable = lastDecRunnable;
+        if (firstIncRunnable != null) mFirstIncRunnables.add(firstIncRunnable);
+        if (lastDecRunnable != null) mLastDecRunnables.add(lastDecRunnable);
         mErrorRunnable = errorRunanable;
     }
 
     /** Increments the ref count */
     public void increment() {
-        if (mCount == 0 && mFirstIncRunnable != null) {
-            mFirstIncRunnable.run();
+        if (mCount == 0 && !mFirstIncRunnables.isEmpty()) {
+            int numRunnables = mFirstIncRunnables.size();
+            for (int i = 0; i < numRunnables; i++) {
+                mFirstIncRunnables.get(i).run();
+            }
         }
         mCount++;
     }
@@ -65,11 +70,19 @@
         return mIncrementRunnable;
     }
 
+    /** Adds a runnable to the last-decrement runnables list. */
+    public void addLastDecrementRunnable(Runnable r) {
+        mLastDecRunnables.add(r);
+    }
+
     /** Decrements the ref count */
     public void decrement() {
         mCount--;
-        if (mCount == 0 && mLastDecRunnable != null) {
-            mLastDecRunnable.run();
+        if (mCount == 0 && !mLastDecRunnables.isEmpty()) {
+            int numRunnables = mLastDecRunnables.size();
+            for (int i = 0; i < numRunnables; i++) {
+                mLastDecRunnables.get(i).run();
+            }
         } else if (mCount < 0) {
             if (mErrorRunnable != null) {
                 mErrorRunnable.run();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
index 46e6ee9..25875bc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Utilities.java
@@ -20,8 +20,26 @@
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
 /* Common code */
 public class Utilities {
+
+    // Reflection methods for altering shadows
+    private static Method sPropertyMethod;
+    static {
+        try {
+            Class<?> c = Class.forName("android.view.GLES20Canvas");
+            sPropertyMethod = c.getDeclaredMethod("setProperty", String.class, String.class);
+            if (!sPropertyMethod.isAccessible()) sPropertyMethod.setAccessible(true);
+        } catch (ClassNotFoundException e) {
+            e.printStackTrace();
+        } catch (NoSuchMethodException e) {
+            e.printStackTrace();
+        }
+    }
+
     /**
      * Calculates a consistent animation duration (ms) for all animations depending on the movement
      * of the object being animated.
@@ -66,4 +84,10 @@
         int greyscale = colorToGreyscale(color);
         return (greyscale < 128) ? lightRes : darkRes;
     }
+
+    /** Sets some private shadow properties. */
+    public static void setShadowProperty(String property, String value)
+            throws IllegalAccessException, InvocationTargetException {
+        sPropertyMethod.invoke(null, property, value);
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
index c861d2c..cadfc56 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/FullScreenTransitionView.java
@@ -23,6 +23,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.view.View;
 import android.view.ViewGroup;
@@ -49,8 +50,8 @@
     FullScreenTransitionViewCallbacks mCb;
 
     ImageView mScreenshotView;
-
     Rect mClipRect = new Rect();
+    Paint mLayerPaint = new Paint();
 
     boolean mIsAnimating;
     AnimatorSet mEnterAnimation;
@@ -159,7 +160,7 @@
         int clipBottom = mConfig.systemInsets.top + (int) (ctx.taskRect.height() / scale);
 
         // Enable the HW Layers on the screenshot view
-        mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
 
         // Compose the animation
         mEnterAnimation = new AnimatorSet();
@@ -173,7 +174,7 @@
                 // Mark that we are no longer animating
                 mIsAnimating = false;
                 // Disable the HW Layers on this view
-                setLayerType(View.LAYER_TYPE_NONE, null);
+                setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
 
                 if (Console.Enabled) {
                     Console.logTraceTime(Constants.Log.App.TimeRecentsScreenshotTransition,
@@ -217,7 +218,7 @@
                     // Mark that we are no longer animating
                     mIsAnimating = false;
                     // Disable the HW Layers on the screenshot view
-                    mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null);
+                    mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
                 }
             });
             mEnterAnimation.setDuration(475);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 3e6879d..d06c9d2 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -54,8 +54,8 @@
 
     /** The RecentsView callbacks */
     public interface RecentsViewCallbacks {
-        public void onTaskLaunching(boolean isTaskInStackBounds);
-        public void onExitAnimationTriggered();
+        public void onTaskLaunching();
+        public void onExitToHomeAnimationTriggered();
     }
 
     RecentsConfiguration mConfig;
@@ -194,7 +194,7 @@
         ctx.postAnimationTrigger.decrement();
 
         // Notify of the exit animation
-        mCb.onExitAnimationTriggered();
+        mCb.onExitToHomeAnimationTriggered();
     }
 
     /** Adds the search bar */
@@ -389,13 +389,46 @@
                                final TaskStack stack, final Task task) {
         // Notify any callbacks of the launching of a new task
         if (mCb != null) {
-            boolean isTaskInStackBounds = false;
-            if (stackView != null && tv != null) {
-                isTaskInStackBounds = stackView.isTaskInStackBounds(tv);
-            }
-            mCb.onTaskLaunching(isTaskInStackBounds);
+            mCb.onTaskLaunching();
         }
 
+        // Upfront the processing of the thumbnail
+        TaskViewTransform transform;
+        View sourceView = tv;
+        int offsetX = 0;
+        int offsetY = 0;
+        int stackScroll = stackView.getStackScroll();
+        if (tv == null) {
+            // If there is no actual task view, then use the stack view as the source view
+            // and then offset to the expected transform rect, but bound this to just
+            // outside the display rect (to ensure we don't animate from too far away)
+            sourceView = stackView;
+            transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
+            offsetX = transform.rect.left;
+            offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
+        } else {
+            transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
+        }
+
+        // Compute the thumbnail to scale up from
+        ActivityOptions opts = null;
+        int thumbnailWidth = transform.rect.width();
+        int thumbnailHeight = transform.rect.height();
+        if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
+                task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
+            // Resize the thumbnail to the size of the view that we are animating from
+            Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
+                    Bitmap.Config.ARGB_8888);
+            Canvas c = new Canvas(b);
+            c.drawBitmap(task.thumbnail,
+                    new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
+                    new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
+            c.setBitmap(null);
+            opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
+                    b, offsetX, offsetY);
+        }
+
+        final ActivityOptions launchOpts = opts;
         final Runnable launchRunnable = new Runnable() {
             @Override
             public void run() {
@@ -404,45 +437,10 @@
                             Constants.Log.App.TimeRecentsLaunchKey, "preStartActivity");
                 }
 
-                TaskViewTransform transform;
-                View sourceView = tv;
-                int offsetX = 0;
-                int offsetY = 0;
-                int stackScroll = stackView.getStackScroll();
-                if (tv == null) {
-                    // If there is no actual task view, then use the stack view as the source view
-                    // and then offset to the expected transform rect, but bound this to just
-                    // outside the display rect (to ensure we don't animate from too far away)
-                    sourceView = stackView;
-                    transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
-                    offsetX = transform.rect.left;
-                    offsetY = Math.min(transform.rect.top, mConfig.displayRect.height());
-                } else {
-                    transform = stackView.getStackTransform(stack.indexOfTask(task), stackScroll);
-                }
-
-                // Compute the thumbnail to scale up from
-                ActivityOptions opts = null;
-                int thumbnailWidth = transform.rect.width();
-                int thumbnailHeight = transform.rect.height();
-                if (task.thumbnail != null && thumbnailWidth > 0 && thumbnailHeight > 0 &&
-                        task.thumbnail.getWidth() > 0 && task.thumbnail.getHeight() > 0) {
-                    // Resize the thumbnail to the size of the view that we are animating from
-                    Bitmap b = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight,
-                            Bitmap.Config.ARGB_8888);
-                    Canvas c = new Canvas(b);
-                    c.drawBitmap(task.thumbnail,
-                            new Rect(0, 0, task.thumbnail.getWidth(), task.thumbnail.getHeight()),
-                            new Rect(0, 0, thumbnailWidth, thumbnailHeight), null);
-                    c.setBitmap(null);
-                    opts = ActivityOptions.makeThumbnailScaleUpAnimation(sourceView,
-                            b, offsetX, offsetY);
-                }
-
                 if (task.isActive) {
                     // Bring an active task to the foreground
                     RecentsTaskLoader.getInstance().getSystemServicesProxy()
-                            .moveTaskToFront(task.key.id, opts);
+                            .moveTaskToFront(task.key.id, launchOpts);
                 } else {
                     // Launch the activity anew with the desired animation
                     Intent i = new Intent(task.key.baseIntent);
@@ -451,8 +449,8 @@
                             | Intent.FLAG_ACTIVITY_NEW_TASK);
                     try {
                         UserHandle taskUser = new UserHandle(task.userId);
-                        if (opts != null) {
-                            getContext().startActivityAsUser(i, opts.toBundle(), taskUser);
+                        if (launchOpts != null) {
+                            getContext().startActivityAsUser(i, launchOpts.toBundle(), taskUser);
                         } else {
                             getContext().startActivityAsUser(i, taskUser);
                         }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
new file mode 100644
index 0000000..932c345
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/SystemBarScrimViews.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import com.android.systemui.R;
+import com.android.systemui.recents.RecentsConfiguration;
+
+/** Manages the scrims for the various system bars. */
+public class SystemBarScrimViews {
+
+    RecentsConfiguration mConfig;
+
+    View mStatusBarScrimView;
+    View mNavBarScrimView;
+
+    boolean mHasNavBarScrim;
+    boolean mShouldAnimateStatusBarScrim;
+    boolean mHasStatusBarScrim;
+    boolean mShouldAnimateNavBarScrim;
+
+    public SystemBarScrimViews(RecentsConfiguration config) {
+        mConfig = config;
+    }
+
+    /** Inflates the scrim views */
+    public void inflate(LayoutInflater inflater, ViewGroup parent) {
+        mStatusBarScrimView = inflater.inflate(R.layout.recents_status_bar_scrim, parent, false);
+        mStatusBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.TOP));
+        mNavBarScrimView = inflater.inflate(R.layout.recents_nav_bar_scrim, parent, false);
+        mNavBarScrimView.setLayoutParams(new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.BOTTOM));
+    }
+
+    /**
+     * Prepares the scrim views for animating when entering Recents. This will be called before
+     * the first draw.
+     */
+    public void prepareEnterRecentsAnimation() {
+        mHasNavBarScrim = mConfig.hasNavBarScrim();
+        mShouldAnimateNavBarScrim = mConfig.shouldAnimateNavBarScrim();
+        mHasStatusBarScrim = mConfig.hasStatusBarScrim();
+        mShouldAnimateStatusBarScrim = mConfig.shouldAnimateStatusBarScrim();
+
+        mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
+                View.VISIBLE : View.INVISIBLE);
+        mStatusBarScrimView.setVisibility(mHasStatusBarScrim && !mShouldAnimateStatusBarScrim ?
+                View.VISIBLE : View.INVISIBLE);
+    }
+
+    /**
+     * Starts animating the scrim views when entering Recents.
+     */
+    public void startEnterRecentsAnimation() {
+        if (mHasStatusBarScrim && mShouldAnimateStatusBarScrim) {
+            mStatusBarScrimView.setTranslationY(-mStatusBarScrimView.getMeasuredHeight());
+            mStatusBarScrimView.animate()
+                    .translationY(0)
+                    .setStartDelay(mConfig.taskBarEnterAnimDelay)
+                    .setDuration(mConfig.navBarScrimEnterDuration)
+                    .setInterpolator(mConfig.quintOutInterpolator)
+                    .withStartAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mStatusBarScrimView.setVisibility(View.VISIBLE);
+                        }
+                    })
+                    .start();
+        }
+        if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
+            mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
+            mNavBarScrimView.animate()
+                    .translationY(0)
+                    .setStartDelay(mConfig.taskBarEnterAnimDelay)
+                    .setDuration(mConfig.navBarScrimEnterDuration)
+                    .setInterpolator(mConfig.quintOutInterpolator)
+                    .withStartAction(new Runnable() {
+                        @Override
+                        public void run() {
+                            mNavBarScrimView.setVisibility(View.VISIBLE);
+                        }
+                    })
+                    .start();
+        }
+    }
+
+    /**
+     * Starts animating the scrim views when leaving Recents (either via launching a task, or
+     * going home).
+     */
+    public void startExitRecentsAnimation() {
+        if (mHasNavBarScrim && mShouldAnimateNavBarScrim) {
+            mNavBarScrimView.animate()
+                    .translationY(mNavBarScrimView.getMeasuredHeight())
+                    .setStartDelay(0)
+                    .setDuration(mConfig.taskBarExitAnimDuration)
+                    .setInterpolator(mConfig.fastOutSlowInInterpolator)
+                    .start();
+        }
+    }
+
+    /** Returns the status bar scrim view. */
+    public View getStatusBarScrimView() {
+        return mStatusBarScrimView;
+    }
+
+    /** Returns the nav bar scrim view. */
+    public View getNavBarScrimView() {
+        return mNavBarScrimView;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
index 82d6220..1ef58ad 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskBarView.java
@@ -23,6 +23,7 @@
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffXfermode;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.RippleDrawable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.view.ViewPropertyAnimator;
@@ -50,6 +51,7 @@
     Drawable mLightDismissDrawable;
     Drawable mDarkDismissDrawable;
 
+    Paint mLayerPaint = new Paint();
     static Paint sHighlightPaint;
 
     public TaskBarView(Context context) {
@@ -91,6 +93,13 @@
         mApplicationIcon = (ImageView) findViewById(R.id.application_icon);
         mActivityDescription = (TextView) findViewById(R.id.activity_description);
         mDismissButton = (ImageView) findViewById(R.id.dismiss_task);
+
+        // Hide the backgrounds if they are ripple drawables
+        if (!Constants.DebugFlags.App.EnableTaskFiltering) {
+            if (mApplicationIcon.getBackground() instanceof RippleDrawable) {
+                mApplicationIcon.setBackground(null);
+            }
+        }
     }
 
     @Override
@@ -142,16 +151,14 @@
         mActivityDescription.setText(t.activityLabel);
         // Try and apply the system ui tint
         int tint = t.colorPrimary;
-        if (Constants.DebugFlags.App.EnableTaskBarThemeColors && tint != 0) {
-            setBackgroundColor(tint);
-            mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint,
-                    mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor));
-            mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint,
-                    mLightDismissDrawable, mDarkDismissDrawable));
-        } else {
-            setBackgroundColor(mConfig.taskBarViewDefaultBackgroundColor);
-            mActivityDescription.setTextColor(mConfig.taskBarViewDefaultTextColor);
+        if (!Constants.DebugFlags.App.EnableTaskBarThemeColors || tint == 0) {
+            tint = mConfig.taskBarViewDefaultBackgroundColor;
         }
+        setBackgroundColor(tint);
+        mActivityDescription.setTextColor(Utilities.getIdealColorForBackgroundColor(tint,
+                mConfig.taskBarViewLightTextColor, mConfig.taskBarViewDarkTextColor));
+        mDismissButton.setImageDrawable(Utilities.getIdealResourceForBackgroundColor(tint,
+                mLightDismissDrawable, mDarkDismissDrawable));
     }
 
     /** Unbinds the bar view from the task */
@@ -237,11 +244,11 @@
 
     /** Enable the hw layers on this task view */
     void enableHwLayers() {
-        mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        mDismissButton.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
     }
 
     /** Disable the hw layers on this task view */
     void disableHwLayers() {
-        mDismissButton.setLayerType(View.LAYER_TYPE_NONE, null);
+        mDismissButton.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index e0a12b7..74bc526 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -26,13 +26,9 @@
 import android.graphics.Canvas;
 import android.graphics.Rect;
 import android.graphics.Region;
-import android.util.Pair;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
-import android.view.VelocityTracker;
 import android.view.View;
-import android.view.ViewConfiguration;
-import android.view.ViewParent;
 import android.widget.FrameLayout;
 import android.widget.OverScroller;
 import com.android.systemui.R;
@@ -71,7 +67,7 @@
     TaskStackViewCallbacks mCb;
     ViewPool<TaskView, Task> mViewPool;
     ArrayList<TaskViewTransform> mTaskTransforms = new ArrayList<TaskViewTransform>();
-    DozeTrigger mDozeTrigger;
+    DozeTrigger mUIDozeTrigger;
 
     // The various rects that define the stack view
     Rect mRect = new Rect();
@@ -100,6 +96,16 @@
     Rect mTmpRect2 = new Rect();
     LayoutInflater mInflater;
 
+    Runnable mReturnAllViewsToPoolRunnable = new Runnable() {
+        @Override
+        public void run() {
+            int childCount = getChildCount();
+            for (int i = childCount - 1; i >= 0; i--) {
+                mViewPool.returnViewToPool((TaskView) getChildAt(i));
+            }
+        }
+    };
+
     public TaskStackView(Context context, TaskStack stack) {
         super(context);
         mConfig = RecentsConfiguration.getInstance();
@@ -109,7 +115,7 @@
         mTouchHandler = new TaskStackViewTouchHandler(context, this);
         mViewPool = new ViewPool<TaskView, Task>(context, this);
         mInflater = LayoutInflater.from(context);
-        mDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
+        mUIDozeTrigger = new DozeTrigger(mConfig.taskBarDismissDozeDelaySeconds, new Runnable() {
             @Override
             public void run() {
                 // Show the task bar dismiss buttons
@@ -187,12 +193,20 @@
         return null;
     }
 
-    /** Update/get the transform */
+    /** Update/get the transform (creates a new TaskViewTransform) */
     public TaskViewTransform getStackTransform(int indexInStack, int stackScroll) {
         TaskViewTransform transform = new TaskViewTransform();
+        return getStackTransform(indexInStack, stackScroll, transform);
+    }
 
+    /** Update/get the transform */
+    public TaskViewTransform getStackTransform(int indexInStack, int stackScroll,
+                                               TaskViewTransform transformOut) {
         // Return early if we have an invalid index
-        if (indexInStack < 0) return transform;
+        if (indexInStack < 0) {
+            transformOut.reset();
+            return transformOut;
+        }
 
         // Map the items to an continuous position relative to the specified scroll
         int numPeekCards = Constants.Values.TaskStackView.StackPeekNumCards;
@@ -209,35 +223,35 @@
         float scale = Math.max(minScale, Math.min(1f, minScale + 
             ((boundedT + (numPeekCards + 1)) * scaleInc)));
         float scaleYOffset = ((1f - scale) * mTaskRect.height()) / 2;
-        transform.scale = scale;
+        transformOut.scale = scale;
 
         // Set the y translation
         if (boundedT < 0f) {
-            transform.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
+            transformOut.translationY = (int) ((Math.max(-numPeekCards, boundedT) /
                     numPeekCards) * peekHeight - scaleYOffset);
         } else {
-            transform.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
+            transformOut.translationY = (int) (boundedT * overlapHeight - scaleYOffset);
         }
 
         // Set the z translation
         int minZ = mConfig.taskViewTranslationZMinPx;
         int incZ = mConfig.taskViewTranslationZIncrementPx;
-        transform.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
+        transformOut.translationZ = (int) Math.max(minZ, minZ + ((boundedT + numPeekCards) * incZ));
 
         // Set the alphas
-        transform.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
+        transformOut.dismissAlpha = Math.max(-1f, Math.min(0f, t + 1)) + 1f;
 
         // Update the rect and visibility
-        transform.rect.set(mTaskRect);
+        transformOut.rect.set(mTaskRect);
         if (t < -(numPeekCards + 1)) {
-            transform.visible = false;
+            transformOut.visible = false;
         } else {
-            transform.rect.offset(0, transform.translationY);
-            Utilities.scaleRectAboutCenter(transform.rect, transform.scale);
-            transform.visible = Rect.intersects(mRect, transform.rect);
+            transformOut.rect.offset(0, transformOut.translationY);
+            Utilities.scaleRectAboutCenter(transformOut.rect, transformOut.scale);
+            transformOut.visible = Rect.intersects(mRect, transformOut.rect);
         }
-        transform.t = t;
-        return transform;
+        transformOut.t = t;
+        return transformOut;
     }
 
     /**
@@ -250,14 +264,25 @@
                                        boolean boundTranslationsToRect) {
         // XXX: Optimization: Use binary search to find the visible range
 
+        int taskTransformCount = taskTransforms.size();
         int taskCount = tasks.size();
         int firstVisibleIndex = -1;
         int lastVisibleIndex = -1;
-        taskTransforms.clear();
-        taskTransforms.ensureCapacity(taskCount);
+
+        // We can reuse the task transforms where possible to reduce object allocation
+        if (taskTransformCount < taskCount) {
+            // If there are less transforms than tasks, then add as many transforms as necessary
+            for (int i = taskTransformCount; i < taskCount; i++) {
+                taskTransforms.add(new TaskViewTransform());
+            }
+        } else if (taskTransformCount > taskCount) {
+            // If there are more transforms than tasks, then just subset the transform list
+            taskTransforms.subList(0, taskCount);
+        }
+
+        // Update the stack transforms
         for (int i = 0; i < taskCount; i++) {
-            TaskViewTransform transform = getStackTransform(i, stackScroll);
-            taskTransforms.add(transform);
+            TaskViewTransform transform = getStackTransform(i, stackScroll, taskTransforms.get(i));
             if (transform.visible) {
                 if (firstVisibleIndex < 0) {
                     firstVisibleIndex = i;
@@ -361,11 +386,13 @@
     /** Sets the current stack scroll */
     public void setStackScroll(int value) {
         mStackScroll = value;
+        mUIDozeTrigger.poke();
         requestSynchronizeStackViewsWithModel();
     }
     /** Sets the current stack scroll without synchronizing the stack view with the model */
     public void setStackScrollRaw(int value) {
         mStackScroll = value;
+        mUIDozeTrigger.poke();
     }
     /** Sets the current stack scroll to the initial state when you first enter recents */
     public void setStackScrollToInitialState() {
@@ -825,7 +852,7 @@
             mAwaitingFirstLayout = false;
 
             // Start dozing
-            mDozeTrigger.startDozing();
+            mUIDozeTrigger.startDozing();
 
             // Prepare the first view for its enter animation
             int offsetTopAlign = -mTaskRect.top;
@@ -885,6 +912,9 @@
             TaskView tv = (TaskView) getChildAt(i);
             tv.startExitToHomeAnimation(ctx);
         }
+
+        // Add a runnable to the post animation ref counter to clear all the views
+        ctx.postAnimationTrigger.addLastDecrementRunnable(mReturnAllViewsToPoolRunnable);
     }
 
     @Override
@@ -899,11 +929,8 @@
 
     /** Pokes the dozer on user interaction. */
     void onUserInteraction() {
-        // If the dozer is not running, then either we have not yet laid out, or it has already
-        // fallen asleep, so just let it rest.
-        if (mDozeTrigger.isDozing()) {
-            mDozeTrigger.poke();
-        }
+        // Poke the doze trigger if it is dozing
+        mUIDozeTrigger.poke();
     }
 
     /**** TaskStackCallbacks Implementation ****/
@@ -917,15 +944,14 @@
     public void onStackTaskRemoved(TaskStack stack, Task t) {
         // Remove the view associated with this task, we can't rely on updateTransforms
         // to work here because the task is no longer in the list
-        int childCount = getChildCount();
-        for (int i = childCount - 1; i >= 0; i--) {
-            TaskView tv = (TaskView) getChildAt(i);
-            if (tv.getTask() == t) {
-                mViewPool.returnViewToPool(tv);
-                break;
-            }
+        TaskView tv = getChildViewForTask(t);
+        if (tv != null) {
+            mViewPool.returnViewToPool(tv);
         }
 
+        // Notify the callback that we've removed the task and it can clean up after it
+        mCb.onTaskRemoved(t);
+
         // Update the min/max scroll and animate other task views into their new positions
         updateMinMaxScroll(true);
         int movement = (int) (Constants.Values.TaskStackView.StackOverlapPct * mTaskRect.height());
@@ -954,10 +980,11 @@
     int getExitTransformsForFilterAnimation(ArrayList<Task> curTasks,
                         ArrayList<TaskViewTransform> curTaskTransforms,
                         ArrayList<Task> tasks, ArrayList<TaskViewTransform> taskTransforms,
-                        HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut,
+                        HashMap<TaskView, TaskViewTransform> childViewTransformsOut,
                         ArrayList<TaskView> childrenToRemoveOut) {
         // Animate all of the existing views out of view (if they are not in the visible range in
         // the new stack) or to their final positions in the new stack
+        int offset = 0;
         int movement = 0;
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
@@ -982,10 +1009,12 @@
                 movement = Math.max(movement, Math.abs(toTransform.translationY -
                         (int) tv.getTranslationY()));
             }
-            childViewTransformsOut.put(tv, new Pair(0, toTransform));
+
+            toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay;
+            childViewTransformsOut.put(tv, toTransform);
+            offset++;
         }
-        return Utilities.calculateTranslationAnimationDuration(movement,
-                mConfig.filteringCurrentViewsMinAnimDuration);
+        return mConfig.filteringCurrentViewsAnimDuration;
     }
 
     /**
@@ -994,7 +1023,7 @@
      */
     int getEnterTransformsForFilterAnimation(ArrayList<Task> tasks,
                          ArrayList<TaskViewTransform> taskTransforms,
-                         HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransformsOut) {
+                         HashMap<TaskView, TaskViewTransform> childViewTransformsOut) {
         int offset = 0;
         int movement = 0;
         int taskCount = tasks.size();
@@ -1012,9 +1041,8 @@
                     tv.prepareTaskTransformForFilterTaskHidden(fromTransform);
                     tv.updateViewPropertiesToTaskTransform(fromTransform, 0);
 
-                    int startDelay = offset *
-                            Constants.Values.TaskStackView.FilterStartDelay;
-                    childViewTransformsOut.put(tv, new Pair(startDelay, toTransform));
+                    toTransform.startDelay = offset * Constants.Values.TaskStackView.FilterStartDelay;
+                    childViewTransformsOut.put(tv, toTransform);
 
                     // Use the movement of the new views to calculate the duration of the animation
                     movement = Math.max(movement,
@@ -1023,8 +1051,7 @@
                 }
             }
         }
-        return Utilities.calculateTranslationAnimationDuration(movement,
-                mConfig.filteringNewViewsMinAnimDuration);
+        return mConfig.filteringNewViewsAnimDuration;
     }
 
     /** Orchestrates the animations of the current child views and any new views. */
@@ -1035,8 +1062,8 @@
         // Calculate the transforms to animate out all the existing views if they are not in the
         // new visible range (or to their final positions in the stack if they are)
         final ArrayList<TaskView> childrenToRemove = new ArrayList<TaskView>();
-        final HashMap<TaskView, Pair<Integer, TaskViewTransform>> childViewTransforms =
-                new HashMap<TaskView, Pair<Integer, TaskViewTransform>>();
+        final HashMap<TaskView, TaskViewTransform> childViewTransforms =
+                new HashMap<TaskView, TaskViewTransform>();
         int duration = getExitTransformsForFilterAnimation(curTasks, curTaskTransforms, tasks,
                 taskTransforms, childViewTransforms, childrenToRemove);
 
@@ -1051,10 +1078,9 @@
 
         // Animate all the views to their final transforms
         for (final TaskView tv : childViewTransforms.keySet()) {
-            Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv);
+            TaskViewTransform t = childViewTransforms.get(tv);
             tv.animate().cancel();
             tv.animate()
-                    .setStartDelay(t.first)
                     .withEndAction(new Runnable() {
                         @Override
                         public void run() {
@@ -1071,15 +1097,14 @@
                                     int duration = getEnterTransformsForFilterAnimation(tasks,
                                             taskTransforms, childViewTransforms);
                                     for (final TaskView tv : childViewTransforms.keySet()) {
-                                        Pair<Integer, TaskViewTransform> t = childViewTransforms.get(tv);
-                                        tv.animate().setStartDelay(t.first);
-                                        tv.updateViewPropertiesToTaskTransform(t.second, duration);
+                                        TaskViewTransform t = childViewTransforms.get(tv);
+                                        tv.updateViewPropertiesToTaskTransform(t, duration);
                                     }
                                 }
                             }
                         }
                     });
-            tv.updateViewPropertiesToTaskTransform(t.second, duration);
+            tv.updateViewPropertiesToTaskTransform(t, duration);
         }
     }
 
@@ -1175,7 +1200,6 @@
         // Request that this tasks's data be filled
         RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
         loader.loadTaskData(task);
-
         // Find the index where this task should be placed in the children
         int insertIndex = -1;
         int childCount = getChildCount();
@@ -1192,7 +1216,7 @@
         tv.setClipViewInStack(true);
 
         // If the doze trigger has already fired, then update the state for this task view
-        if (mDozeTrigger.hasTriggered()) {
+        if (mUIDozeTrigger.hasTriggered()) {
             tv.setNoUserInteractionState();
         }
 
@@ -1257,8 +1281,6 @@
         Task task = tv.getTask();
         // Remove the task from the view
         mStack.removeTask(task);
-        // Notify the callback that we've removed the task and it can clean up after it
-        mCb.onTaskRemoved(task);
     }
 
     /**** View.OnClickListener Implementation ****/
@@ -1272,6 +1294,9 @@
                     task + " cb: " + mCb);
         }
 
+        // Cancel any doze triggers
+        mUIDozeTrigger.stopDozing();
+
         if (mCb != null) {
             mCb.onTaskLaunched(this, tv, mStack, task);
         }
@@ -1302,393 +1327,4 @@
             }
         }
     }
-}
-
-/* Handles touch events */
-class TaskStackViewTouchHandler implements SwipeHelper.Callback {
-    static int INACTIVE_POINTER_ID = -1;
-
-    TaskStackView mSv;
-    VelocityTracker mVelocityTracker;
-
-    boolean mIsScrolling;
-
-    int mInitialMotionX, mInitialMotionY;
-    int mLastMotionX, mLastMotionY;
-    int mActivePointerId = INACTIVE_POINTER_ID;
-    TaskView mActiveTaskView = null;
-
-    int mTotalScrollMotion;
-    int mMinimumVelocity;
-    int mMaximumVelocity;
-    // The scroll touch slop is used to calculate when we start scrolling
-    int mScrollTouchSlop;
-    // The page touch slop is used to calculate when we start swiping
-    float mPagingTouchSlop;
-
-    SwipeHelper mSwipeHelper;
-    boolean mInterceptedBySwipeHelper;
-
-    public TaskStackViewTouchHandler(Context context, TaskStackView sv) {
-        ViewConfiguration configuration = ViewConfiguration.get(context);
-        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
-        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
-        mScrollTouchSlop = configuration.getScaledTouchSlop();
-        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
-        mSv = sv;
-
-
-        float densityScale = context.getResources().getDisplayMetrics().density;
-        mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
-        mSwipeHelper.setMinAlpha(1f);
-    }
-
-    /** Velocity tracker helpers */
-    void initOrResetVelocityTracker() {
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        } else {
-            mVelocityTracker.clear();
-        }
-    }
-    void initVelocityTrackerIfNotExists() {
-        if (mVelocityTracker == null) {
-            mVelocityTracker = VelocityTracker.obtain();
-        }
-    }
-    void recycleVelocityTracker() {
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-    }
-
-    /** Returns the view at the specified coordinates */
-    TaskView findViewAtPoint(int x, int y) {
-        int childCount = mSv.getChildCount();
-        for (int i = childCount - 1; i >= 0; i--) {
-            TaskView tv = (TaskView) mSv.getChildAt(i);
-            if (tv.getVisibility() == View.VISIBLE) {
-                if (mSv.isTransformedTouchPointInView(x, y, tv)) {
-                    return tv;
-                }
-            }
-        }
-        return null;
-    }
-
-    /** Touch preprocessing for handling below */
-    public boolean onInterceptTouchEvent(MotionEvent ev) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.TouchEvents,
-                    "[TaskStackViewTouchHandler|interceptTouchEvent]",
-                    Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
-        }
-
-        // Return early if we have no children
-        boolean hasChildren = (mSv.getChildCount() > 0);
-        if (!hasChildren) {
-            return false;
-        }
-
-        // Pass through to swipe helper if we are swiping
-        mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
-        if (mInterceptedBySwipeHelper) {
-            return true;
-        }
-
-        boolean wasScrolling = !mSv.mScroller.isFinished() ||
-                (mSv.mScrollAnimator != null && mSv.mScrollAnimator.isRunning());
-        int action = ev.getAction();
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_DOWN: {
-                // Save the touch down info
-                mInitialMotionX = mLastMotionX = (int) ev.getX();
-                mInitialMotionY = mLastMotionY = (int) ev.getY();
-                mActivePointerId = ev.getPointerId(0);
-                mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
-                // Stop the current scroll if it is still flinging
-                mSv.abortScroller();
-                mSv.abortBoundScrollAnimation();
-                // Initialize the velocity tracker
-                initOrResetVelocityTracker();
-                mVelocityTracker.addMovement(ev);
-                // Check if the scroller is finished yet
-                mIsScrolling = !mSv.mScroller.isFinished();
-                break;
-            }
-            case MotionEvent.ACTION_MOVE: {
-                if (mActivePointerId == INACTIVE_POINTER_ID) break;
-
-                int activePointerIndex = ev.findPointerIndex(mActivePointerId);
-                int y = (int) ev.getY(activePointerIndex);
-                int x = (int) ev.getX(activePointerIndex);
-                if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
-                    // Save the touch move info
-                    mIsScrolling = true;
-                    // Initialize the velocity tracker if necessary
-                    initVelocityTrackerIfNotExists();
-                    mVelocityTracker.addMovement(ev);
-                    // Disallow parents from intercepting touch events
-                    final ViewParent parent = mSv.getParent();
-                    if (parent != null) {
-                        parent.requestDisallowInterceptTouchEvent(true);
-                    }
-                    // Enable HW layers
-                    mSv.addHwLayersRefCount("stackScroll");
-                }
-
-                mLastMotionX = x;
-                mLastMotionY = y;
-                break;
-            }
-            case MotionEvent.ACTION_CANCEL:
-            case MotionEvent.ACTION_UP: {
-                // Animate the scroll back if we've cancelled
-                mSv.animateBoundScroll();
-                // Disable HW layers
-                if (mIsScrolling) {
-                    mSv.decHwLayersRefCount("stackScroll");
-                }
-                // Reset the drag state and the velocity tracker
-                mIsScrolling = false;
-                mActivePointerId = INACTIVE_POINTER_ID;
-                mActiveTaskView = null;
-                mTotalScrollMotion = 0;
-                recycleVelocityTracker();
-                break;
-            }
-        }
-
-        return wasScrolling || mIsScrolling;
-    }
-
-    /** Handles touch events once we have intercepted them */
-    public boolean onTouchEvent(MotionEvent ev) {
-        if (Console.Enabled) {
-            Console.log(Constants.Log.UI.TouchEvents,
-                    "[TaskStackViewTouchHandler|touchEvent]",
-                    Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
-        }
-
-        // Short circuit if we have no children
-        boolean hasChildren = (mSv.getChildCount() > 0);
-        if (!hasChildren) {
-            return false;
-        }
-
-        // Pass through to swipe helper if we are swiping
-        if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
-            return true;
-        }
-
-        // Update the velocity tracker
-        initVelocityTrackerIfNotExists();
-        mVelocityTracker.addMovement(ev);
-
-        int action = ev.getAction();
-        switch (action & MotionEvent.ACTION_MASK) {
-            case MotionEvent.ACTION_DOWN: {
-                // Save the touch down info
-                mInitialMotionX = mLastMotionX = (int) ev.getX();
-                mInitialMotionY = mLastMotionY = (int) ev.getY();
-                mActivePointerId = ev.getPointerId(0);
-                mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
-                // Stop the current scroll if it is still flinging
-                mSv.abortScroller();
-                mSv.abortBoundScrollAnimation();
-                // Initialize the velocity tracker
-                initOrResetVelocityTracker();
-                mVelocityTracker.addMovement(ev);
-                // Disallow parents from intercepting touch events
-                final ViewParent parent = mSv.getParent();
-                if (parent != null) {
-                    parent.requestDisallowInterceptTouchEvent(true);
-                }
-                break;
-            }
-            case MotionEvent.ACTION_POINTER_DOWN: {
-                final int index = ev.getActionIndex();
-                mActivePointerId = ev.getPointerId(index);
-                mLastMotionX = (int) ev.getX(index);
-                mLastMotionY = (int) ev.getY(index);
-                break;
-            }
-            case MotionEvent.ACTION_MOVE: {
-                if (mActivePointerId == INACTIVE_POINTER_ID) break;
-
-                int activePointerIndex = ev.findPointerIndex(mActivePointerId);
-                int x = (int) ev.getX(activePointerIndex);
-                int y = (int) ev.getY(activePointerIndex);
-                int yTotal = Math.abs(y - mInitialMotionY);
-                int deltaY = mLastMotionY - y;
-                if (!mIsScrolling) {
-                    if (yTotal > mScrollTouchSlop) {
-                        mIsScrolling = true;
-                        // Initialize the velocity tracker
-                        initOrResetVelocityTracker();
-                        mVelocityTracker.addMovement(ev);
-                        // Disallow parents from intercepting touch events
-                        final ViewParent parent = mSv.getParent();
-                        if (parent != null) {
-                            parent.requestDisallowInterceptTouchEvent(true);
-                        }
-                        // Enable HW layers
-                        mSv.addHwLayersRefCount("stackScroll");
-                    }
-                }
-                if (mIsScrolling) {
-                    int curStackScroll = mSv.getStackScroll();
-                    int overScrollAmount = mSv.getScrollAmountOutOfBounds(curStackScroll + deltaY);
-                    if (overScrollAmount != 0) {
-                        // Bound the overscroll to a fixed amount, and inversely scale the y-movement
-                        // relative to how close we are to the max overscroll
-                        float maxOverScroll = mSv.mTaskRect.height() / 3f;
-                        deltaY = Math.round(deltaY * (1f - (Math.min(maxOverScroll, overScrollAmount)
-                                / maxOverScroll)));
-                    }
-                    mSv.setStackScroll(curStackScroll + deltaY);
-                    if (mSv.isScrollOutOfBounds()) {
-                        mVelocityTracker.clear();
-                    }
-                }
-                mLastMotionX = x;
-                mLastMotionY = y;
-                mTotalScrollMotion += Math.abs(deltaY);
-                break;
-            }
-            case MotionEvent.ACTION_UP: {
-                final VelocityTracker velocityTracker = mVelocityTracker;
-                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
-                int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
-
-                if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
-                    // Enable HW layers on the stack
-                    mSv.addHwLayersRefCount("flingScroll");
-                    // XXX: Make this animation a function of the velocity AND distance
-                    int overscrollRange = (int) (Math.min(1f,
-                            Math.abs((float) velocity / mMaximumVelocity)) *
-                            Constants.Values.TaskStackView.TaskStackOverscrollRange);
-
-                    if (Console.Enabled) {
-                        Console.log(Constants.Log.UI.TouchEvents,
-                                "[TaskStackViewTouchHandler|fling]",
-                                "scroll: " + mSv.getStackScroll() + " velocity: " + velocity +
-                                        " maxVelocity: " + mMaximumVelocity +
-                                        " overscrollRange: " + overscrollRange,
-                                Console.AnsiGreen);
-                    }
-
-                    // Fling scroll
-                    mSv.mScroller.fling(0, mSv.getStackScroll(),
-                            0, -velocity,
-                            0, 0,
-                            mSv.mMinScroll, mSv.mMaxScroll,
-                            0, overscrollRange);
-                    // Invalidate to kick off computeScroll
-                    mSv.invalidate(mSv.mStackRect);
-                } else if (mSv.isScrollOutOfBounds()) {
-                    // Animate the scroll back into bounds
-                    // XXX: Make this animation a function of the velocity OR distance
-                    mSv.animateBoundScroll();
-                }
-
-                if (mIsScrolling) {
-                    // Disable HW layers
-                    mSv.decHwLayersRefCount("stackScroll");
-                }
-                mActivePointerId = INACTIVE_POINTER_ID;
-                mIsScrolling = false;
-                mTotalScrollMotion = 0;
-                recycleVelocityTracker();
-                break;
-            }
-            case MotionEvent.ACTION_POINTER_UP: {
-                int pointerIndex = ev.getActionIndex();
-                int pointerId = ev.getPointerId(pointerIndex);
-                if (pointerId == mActivePointerId) {
-                    // Select a new active pointer id and reset the motion state
-                    final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
-                    mActivePointerId = ev.getPointerId(newPointerIndex);
-                    mLastMotionX = (int) ev.getX(newPointerIndex);
-                    mLastMotionY = (int) ev.getY(newPointerIndex);
-                    mVelocityTracker.clear();
-                }
-                break;
-            }
-            case MotionEvent.ACTION_CANCEL: {
-                if (mIsScrolling) {
-                    // Disable HW layers
-                    mSv.decHwLayersRefCount("stackScroll");
-                }
-                if (mSv.isScrollOutOfBounds()) {
-                    // Animate the scroll back into bounds
-                    // XXX: Make this animation a function of the velocity OR distance
-                    mSv.animateBoundScroll();
-                }
-                mActivePointerId = INACTIVE_POINTER_ID;
-                mIsScrolling = false;
-                mTotalScrollMotion = 0;
-                recycleVelocityTracker();
-                break;
-            }
-        }
-        return true;
-    }
-
-    /**** SwipeHelper Implementation ****/
-
-    @Override
-    public View getChildAtPosition(MotionEvent ev) {
-        return findViewAtPoint((int) ev.getX(), (int) ev.getY());
-    }
-
-    @Override
-    public boolean canChildBeDismissed(View v) {
-        return true;
-    }
-
-    @Override
-    public void onBeginDrag(View v) {
-        // Enable HW layers
-        mSv.addHwLayersRefCount("swipeBegin");
-        // Disable clipping with the stack while we are swiping
-        TaskView tv = (TaskView) v;
-        tv.setClipViewInStack(false);
-        // Disallow parents from intercepting touch events
-        final ViewParent parent = mSv.getParent();
-        if (parent != null) {
-            parent.requestDisallowInterceptTouchEvent(true);
-        }
-    }
-
-    @Override
-    public void onSwipeChanged(View v, float delta) {
-        // Do nothing
-    }
-
-    @Override
-    public void onChildDismissed(View v) {
-        TaskView tv = (TaskView) v;
-        mSv.onTaskDismissed(tv);
-
-        // Re-enable clipping with the stack (we will reuse this view)
-        tv.setClipViewInStack(true);
-
-        // Disable HW layers
-        mSv.decHwLayersRefCount("swipeComplete");
-    }
-
-    @Override
-    public void onSnapBackCompleted(View v) {
-        // Re-enable clipping with the stack
-        TaskView tv = (TaskView) v;
-        tv.setClipViewInStack(true);
-    }
-
-    @Override
-    public void onDragCancelled(View v) {
-        // Disable HW layers
-        mSv.decHwLayersRefCount("swipeCancelled");
-    }
-}
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
new file mode 100644
index 0000000..d2f18ae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackViewTouchHandler.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2014 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.systemui.recents.views;
+
+import android.content.Context;
+import android.view.MotionEvent;
+import android.view.VelocityTracker;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewParent;
+import com.android.systemui.recents.Console;
+import com.android.systemui.recents.Constants;
+
+/* Handles touch events for a TaskStackView. */
+class TaskStackViewTouchHandler implements SwipeHelper.Callback {
+    static int INACTIVE_POINTER_ID = -1;
+
+    TaskStackView mSv;
+    VelocityTracker mVelocityTracker;
+
+    boolean mIsScrolling;
+
+    int mInitialMotionX, mInitialMotionY;
+    int mLastMotionX, mLastMotionY;
+    int mActivePointerId = INACTIVE_POINTER_ID;
+    TaskView mActiveTaskView = null;
+
+    int mTotalScrollMotion;
+    int mMinimumVelocity;
+    int mMaximumVelocity;
+    // The scroll touch slop is used to calculate when we start scrolling
+    int mScrollTouchSlop;
+    // The page touch slop is used to calculate when we start swiping
+    float mPagingTouchSlop;
+
+    SwipeHelper mSwipeHelper;
+    boolean mInterceptedBySwipeHelper;
+
+    public TaskStackViewTouchHandler(Context context, TaskStackView sv) {
+        ViewConfiguration configuration = ViewConfiguration.get(context);
+        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
+        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
+        mScrollTouchSlop = configuration.getScaledTouchSlop();
+        mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
+        mSv = sv;
+
+
+        float densityScale = context.getResources().getDisplayMetrics().density;
+        mSwipeHelper = new SwipeHelper(SwipeHelper.X, this, densityScale, mPagingTouchSlop);
+        mSwipeHelper.setMinAlpha(1f);
+    }
+
+    /** Velocity tracker helpers */
+    void initOrResetVelocityTracker() {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        } else {
+            mVelocityTracker.clear();
+        }
+    }
+    void initVelocityTrackerIfNotExists() {
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+    }
+    void recycleVelocityTracker() {
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+    }
+
+    /** Returns the view at the specified coordinates */
+    TaskView findViewAtPoint(int x, int y) {
+        int childCount = mSv.getChildCount();
+        for (int i = childCount - 1; i >= 0; i--) {
+            TaskView tv = (TaskView) mSv.getChildAt(i);
+            if (tv.getVisibility() == View.VISIBLE) {
+                if (mSv.isTransformedTouchPointInView(x, y, tv)) {
+                    return tv;
+                }
+            }
+        }
+        return null;
+    }
+
+    /** Touch preprocessing for handling below */
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        if (Console.Enabled) {
+            Console.log(Constants.Log.UI.TouchEvents,
+                    "[TaskStackViewTouchHandler|interceptTouchEvent]",
+                    Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
+        }
+
+        // Return early if we have no children
+        boolean hasChildren = (mSv.getChildCount() > 0);
+        if (!hasChildren) {
+            return false;
+        }
+
+        // Pass through to swipe helper if we are swiping
+        mInterceptedBySwipeHelper = mSwipeHelper.onInterceptTouchEvent(ev);
+        if (mInterceptedBySwipeHelper) {
+            return true;
+        }
+
+        boolean wasScrolling = !mSv.mScroller.isFinished() ||
+                (mSv.mScrollAnimator != null && mSv.mScrollAnimator.isRunning());
+        int action = ev.getAction();
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN: {
+                // Save the touch down info
+                mInitialMotionX = mLastMotionX = (int) ev.getX();
+                mInitialMotionY = mLastMotionY = (int) ev.getY();
+                mActivePointerId = ev.getPointerId(0);
+                mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
+                // Stop the current scroll if it is still flinging
+                mSv.abortScroller();
+                mSv.abortBoundScrollAnimation();
+                // Initialize the velocity tracker
+                initOrResetVelocityTracker();
+                mVelocityTracker.addMovement(ev);
+                // Check if the scroller is finished yet
+                mIsScrolling = !mSv.mScroller.isFinished();
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                if (mActivePointerId == INACTIVE_POINTER_ID) break;
+
+                int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                int y = (int) ev.getY(activePointerIndex);
+                int x = (int) ev.getX(activePointerIndex);
+                if (Math.abs(y - mInitialMotionY) > mScrollTouchSlop) {
+                    // Save the touch move info
+                    mIsScrolling = true;
+                    // Initialize the velocity tracker if necessary
+                    initVelocityTrackerIfNotExists();
+                    mVelocityTracker.addMovement(ev);
+                    // Disallow parents from intercepting touch events
+                    final ViewParent parent = mSv.getParent();
+                    if (parent != null) {
+                        parent.requestDisallowInterceptTouchEvent(true);
+                    }
+                    // Enable HW layers
+                    mSv.addHwLayersRefCount("stackScroll");
+                }
+
+                mLastMotionX = x;
+                mLastMotionY = y;
+                break;
+            }
+            case MotionEvent.ACTION_CANCEL:
+            case MotionEvent.ACTION_UP: {
+                // Animate the scroll back if we've cancelled
+                mSv.animateBoundScroll();
+                // Disable HW layers
+                if (mIsScrolling) {
+                    mSv.decHwLayersRefCount("stackScroll");
+                }
+                // Reset the drag state and the velocity tracker
+                mIsScrolling = false;
+                mActivePointerId = INACTIVE_POINTER_ID;
+                mActiveTaskView = null;
+                mTotalScrollMotion = 0;
+                recycleVelocityTracker();
+                break;
+            }
+        }
+
+        return wasScrolling || mIsScrolling;
+    }
+
+    /** Handles touch events once we have intercepted them */
+    public boolean onTouchEvent(MotionEvent ev) {
+        if (Console.Enabled) {
+            Console.log(Constants.Log.UI.TouchEvents,
+                    "[TaskStackViewTouchHandler|touchEvent]",
+                    Console.motionEventActionToString(ev.getAction()), Console.AnsiBlue);
+        }
+
+        // Short circuit if we have no children
+        boolean hasChildren = (mSv.getChildCount() > 0);
+        if (!hasChildren) {
+            return false;
+        }
+
+        // Pass through to swipe helper if we are swiping
+        if (mInterceptedBySwipeHelper && mSwipeHelper.onTouchEvent(ev)) {
+            return true;
+        }
+
+        // Update the velocity tracker
+        initVelocityTrackerIfNotExists();
+        mVelocityTracker.addMovement(ev);
+
+        int action = ev.getAction();
+        switch (action & MotionEvent.ACTION_MASK) {
+            case MotionEvent.ACTION_DOWN: {
+                // Save the touch down info
+                mInitialMotionX = mLastMotionX = (int) ev.getX();
+                mInitialMotionY = mLastMotionY = (int) ev.getY();
+                mActivePointerId = ev.getPointerId(0);
+                mActiveTaskView = findViewAtPoint(mLastMotionX, mLastMotionY);
+                // Stop the current scroll if it is still flinging
+                mSv.abortScroller();
+                mSv.abortBoundScrollAnimation();
+                // Initialize the velocity tracker
+                initOrResetVelocityTracker();
+                mVelocityTracker.addMovement(ev);
+                // Disallow parents from intercepting touch events
+                final ViewParent parent = mSv.getParent();
+                if (parent != null) {
+                    parent.requestDisallowInterceptTouchEvent(true);
+                }
+                break;
+            }
+            case MotionEvent.ACTION_POINTER_DOWN: {
+                final int index = ev.getActionIndex();
+                mActivePointerId = ev.getPointerId(index);
+                mLastMotionX = (int) ev.getX(index);
+                mLastMotionY = (int) ev.getY(index);
+                break;
+            }
+            case MotionEvent.ACTION_MOVE: {
+                if (mActivePointerId == INACTIVE_POINTER_ID) break;
+
+                int activePointerIndex = ev.findPointerIndex(mActivePointerId);
+                int x = (int) ev.getX(activePointerIndex);
+                int y = (int) ev.getY(activePointerIndex);
+                int yTotal = Math.abs(y - mInitialMotionY);
+                int deltaY = mLastMotionY - y;
+                if (!mIsScrolling) {
+                    if (yTotal > mScrollTouchSlop) {
+                        mIsScrolling = true;
+                        // Initialize the velocity tracker
+                        initOrResetVelocityTracker();
+                        mVelocityTracker.addMovement(ev);
+                        // Disallow parents from intercepting touch events
+                        final ViewParent parent = mSv.getParent();
+                        if (parent != null) {
+                            parent.requestDisallowInterceptTouchEvent(true);
+                        }
+                        // Enable HW layers
+                        mSv.addHwLayersRefCount("stackScroll");
+                    }
+                }
+                if (mIsScrolling) {
+                    int curStackScroll = mSv.getStackScroll();
+                    int overScrollAmount = mSv.getScrollAmountOutOfBounds(curStackScroll + deltaY);
+                    if (overScrollAmount != 0) {
+                        // Bound the overscroll to a fixed amount, and inversely scale the y-movement
+                        // relative to how close we are to the max overscroll
+                        float maxOverScroll = mSv.mTaskRect.height() / 3f;
+                        deltaY = Math.round(deltaY * (1f - (Math.min(maxOverScroll, overScrollAmount)
+                                / maxOverScroll)));
+                    }
+                    mSv.setStackScroll(curStackScroll + deltaY);
+                    if (mSv.isScrollOutOfBounds()) {
+                        mVelocityTracker.clear();
+                    }
+                }
+                mLastMotionX = x;
+                mLastMotionY = y;
+                mTotalScrollMotion += Math.abs(deltaY);
+                break;
+            }
+            case MotionEvent.ACTION_UP: {
+                final VelocityTracker velocityTracker = mVelocityTracker;
+                velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
+                int velocity = (int) velocityTracker.getYVelocity(mActivePointerId);
+
+                if (mIsScrolling && (Math.abs(velocity) > mMinimumVelocity)) {
+                    // Enable HW layers on the stack
+                    mSv.addHwLayersRefCount("flingScroll");
+                    // XXX: Make this animation a function of the velocity AND distance
+                    int overscrollRange = (int) (Math.min(1f,
+                            Math.abs((float) velocity / mMaximumVelocity)) *
+                            Constants.Values.TaskStackView.TaskStackOverscrollRange);
+
+                    if (Console.Enabled) {
+                        Console.log(Constants.Log.UI.TouchEvents,
+                                "[TaskStackViewTouchHandler|fling]",
+                                "scroll: " + mSv.getStackScroll() + " velocity: " + velocity +
+                                        " maxVelocity: " + mMaximumVelocity +
+                                        " overscrollRange: " + overscrollRange,
+                                Console.AnsiGreen);
+                    }
+
+                    // Fling scroll
+                    mSv.mScroller.fling(0, mSv.getStackScroll(),
+                            0, -velocity,
+                            0, 0,
+                            mSv.mMinScroll, mSv.mMaxScroll,
+                            0, overscrollRange);
+                    // Invalidate to kick off computeScroll
+                    mSv.invalidate(mSv.mStackRect);
+                } else if (mSv.isScrollOutOfBounds()) {
+                    // Animate the scroll back into bounds
+                    // XXX: Make this animation a function of the velocity OR distance
+                    mSv.animateBoundScroll();
+                }
+
+                if (mIsScrolling) {
+                    // Disable HW layers
+                    mSv.decHwLayersRefCount("stackScroll");
+                }
+                mActivePointerId = INACTIVE_POINTER_ID;
+                mIsScrolling = false;
+                mTotalScrollMotion = 0;
+                recycleVelocityTracker();
+                break;
+            }
+            case MotionEvent.ACTION_POINTER_UP: {
+                int pointerIndex = ev.getActionIndex();
+                int pointerId = ev.getPointerId(pointerIndex);
+                if (pointerId == mActivePointerId) {
+                    // Select a new active pointer id and reset the motion state
+                    final int newPointerIndex = (pointerIndex == 0) ? 1 : 0;
+                    mActivePointerId = ev.getPointerId(newPointerIndex);
+                    mLastMotionX = (int) ev.getX(newPointerIndex);
+                    mLastMotionY = (int) ev.getY(newPointerIndex);
+                    mVelocityTracker.clear();
+                }
+                break;
+            }
+            case MotionEvent.ACTION_CANCEL: {
+                if (mIsScrolling) {
+                    // Disable HW layers
+                    mSv.decHwLayersRefCount("stackScroll");
+                }
+                if (mSv.isScrollOutOfBounds()) {
+                    // Animate the scroll back into bounds
+                    // XXX: Make this animation a function of the velocity OR distance
+                    mSv.animateBoundScroll();
+                }
+                mActivePointerId = INACTIVE_POINTER_ID;
+                mIsScrolling = false;
+                mTotalScrollMotion = 0;
+                recycleVelocityTracker();
+                break;
+            }
+        }
+        return true;
+    }
+
+    /**** SwipeHelper Implementation ****/
+
+    @Override
+    public View getChildAtPosition(MotionEvent ev) {
+        return findViewAtPoint((int) ev.getX(), (int) ev.getY());
+    }
+
+    @Override
+    public boolean canChildBeDismissed(View v) {
+        return true;
+    }
+
+    @Override
+    public void onBeginDrag(View v) {
+        // Enable HW layers
+        mSv.addHwLayersRefCount("swipeBegin");
+        // Disable clipping with the stack while we are swiping
+        TaskView tv = (TaskView) v;
+        tv.setClipViewInStack(false);
+        // Disallow parents from intercepting touch events
+        final ViewParent parent = mSv.getParent();
+        if (parent != null) {
+            parent.requestDisallowInterceptTouchEvent(true);
+        }
+    }
+
+    @Override
+    public void onSwipeChanged(View v, float delta) {
+        // Do nothing
+    }
+
+    @Override
+    public void onChildDismissed(View v) {
+        TaskView tv = (TaskView) v;
+        mSv.onTaskDismissed(tv);
+
+        // Re-enable clipping with the stack (we will reuse this view)
+        tv.setClipViewInStack(true);
+
+        // Disable HW layers
+        mSv.decHwLayersRefCount("swipeComplete");
+    }
+
+    @Override
+    public void onSnapBackCompleted(View v) {
+        // Re-enable clipping with the stack
+        TaskView tv = (TaskView) v;
+        tv.setClipViewInStack(true);
+    }
+
+    @Override
+    public void onDragCancelled(View v) {
+        // Disable HW layers
+        mSv.decHwLayersRefCount("swipeCancelled");
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index 09dc1c8..cfba74c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -22,6 +22,7 @@
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Outline;
+import android.graphics.Paint;
 import android.graphics.Path;
 import android.graphics.Point;
 import android.graphics.Rect;
@@ -63,6 +64,7 @@
     Point mLastTouchDown = new Point();
     Path mRoundedRectClipPath = new Path();
     Rect mTmpRect = new Rect();
+    Paint mLayerPaint = new Paint();
 
     TaskThumbnailView mThumbnailView;
     TaskBarView mBarView;
@@ -200,7 +202,7 @@
             if (useLayers) {
                 anim.withLayer();
             }
-            anim.setStartDelay(0)
+            anim.setStartDelay(toTransform.startDelay)
                 .setDuration(duration)
                 .setInterpolator(mConfig.fastOutSlowInInterpolator)
                 .start();
@@ -246,6 +248,7 @@
         // Fade the view out and slide it away
         toTransform.alpha = 0f;
         toTransform.translationY += 200;
+        toTransform.translationZ = 0;
     }
 
     /**
@@ -440,13 +443,13 @@
 
     /** Enable the hw layers on this task view */
     void enableHwLayers() {
-        mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+        mThumbnailView.setLayerType(View.LAYER_TYPE_HARDWARE, mLayerPaint);
         mBarView.enableHwLayers();
     }
 
     /** Disable the hw layers on this task view */
     void disableHwLayers() {
-        mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, null);
+        mThumbnailView.setLayerType(View.LAYER_TYPE_NONE, mLayerPaint);
         mBarView.disableHwLayers();
     }
 
@@ -583,19 +586,25 @@
     }
 
     @Override
-    public void onClick(View v) {
-        if (v == mBarView.mApplicationIcon) {
-            mCb.onTaskIconClicked(this);
-        } else if (v == mBarView.mDismissButton) {
-            // Animate out the view and call the callback
-            final TaskView tv = this;
-            startDeleteTaskAnimation(new Runnable() {
-                @Override
-                public void run() {
-                    mCb.onTaskDismissed(tv);
+    public void onClick(final View v) {
+        // We purposely post the handler delayed to allow for the touch feedback to draw
+        final TaskView tv = this;
+        postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                if (v == mBarView.mApplicationIcon) {
+                    mCb.onTaskIconClicked(tv);
+                } else if (v == mBarView.mDismissButton) {
+                    // Animate out the view and call the callback
+                    startDeleteTaskAnimation(new Runnable() {
+                        @Override
+                        public void run() {
+                            mCb.onTaskDismissed(tv);
+                        }
+                    });
                 }
-            });
-        }
+            }
+        }, 125);
     }
 
     @Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
index 6c420e1..b351b03 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewTransform.java
@@ -21,6 +21,7 @@
 
 /* The transform state for a task view */
 public class TaskViewTransform {
+    public int startDelay = 0;
     public int translationY = 0;
     public int translationZ = 0;
     public float scale = 1f;
@@ -28,13 +29,14 @@
     public float dismissAlpha = 1f;
     public boolean visible = false;
     public Rect rect = new Rect();
-    float t;
+    float t = 0f;
 
     public TaskViewTransform() {
         // Do nothing
     }
 
     public TaskViewTransform(TaskViewTransform o) {
+        startDelay = o.startDelay;
         translationY = o.translationY;
         translationZ = o.translationZ;
         scale = o.scale;
@@ -45,6 +47,19 @@
         t = o.t;
     }
 
+    /** Resets the current transform */
+    public void reset() {
+        startDelay = 0;
+        translationY = 0;
+        translationZ = 0;
+        scale = 1f;
+        alpha = 1f;
+        dismissAlpha = 1f;
+        visible = false;
+        rect.setEmpty();
+        t = 0f;
+    }
+
     /** Convenience functions to compare against current property values */
     public boolean hasAlphaChangedFrom(float v) {
         return (Float.compare(alpha, v) != 0);
@@ -64,8 +79,8 @@
 
     @Override
     public String toString() {
-        return "TaskViewTransform y: " + translationY + " z: " + translationZ + " scale: " + scale +
-                " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
+        return "TaskViewTransform delay: " + startDelay + " y: " + translationY + " z: " + translationZ +
+                " scale: " + scale + " alpha: " + alpha + " visible: " + visible + " rect: " + rect +
                 " dismissAlpha: " + dismissAlpha;
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index 8d19f50..f6f78e9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -119,6 +119,7 @@
 
     private NotificationBackgroundView mBackgroundNormal;
     private NotificationBackgroundView mBackgroundDimmed;
+    private NotificationScrimView mScrimView;
     private ObjectAnimator mBackgroundAnimator;
     private RectF mAppearAnimationRect = new RectF();
     private PorterDuffColorFilter mAppearAnimationFilter;
@@ -153,6 +154,7 @@
         mBackgroundDimmed = (NotificationBackgroundView) findViewById(R.id.backgroundDimmed);
         updateBackground();
         updateBackgroundResources();
+        mScrimView = (NotificationScrimView) findViewById(R.id.scrim_view);
     }
 
     private final Runnable mTapTimeoutRunnable = new Runnable() {
@@ -379,6 +381,7 @@
         setPivotY(actualHeight / 2);
         mBackgroundNormal.setActualHeight(actualHeight);
         mBackgroundDimmed.setActualHeight(actualHeight);
+        mScrimView.setActualHeight(actualHeight);
     }
 
     @Override
@@ -386,6 +389,7 @@
         super.setClipTopAmount(clipTopAmount);
         mBackgroundNormal.setClipTopAmount(clipTopAmount);
         mBackgroundDimmed.setClipTopAmount(clipTopAmount);
+        mScrimView.setClipTopAmount(clipTopAmount);
     }
 
     @Override
@@ -405,6 +409,11 @@
         }
     }
 
+    @Override
+    public void setScrimAmount(float scrimAmount) {
+        mScrimView.setAlpha(scrimAmount);
+    }
+
     private void startAppearAnimation(boolean isAppearing,
             float translationDirection, long delay, final Runnable onFinishedRunnable) {
         if (mAppearAnimator != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 20684a1..c621f31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -94,7 +94,6 @@
     public static final String TAG = "StatusBar";
     public static final boolean DEBUG = false;
     public static final boolean MULTIUSER_DEBUG = false;
-    private static final boolean USE_NOTIFICATION_LISTENER = true;
 
     protected static final int MSG_SHOW_RECENT_APPS = 1019;
     protected static final int MSG_HIDE_RECENT_APPS = 1020;
@@ -298,7 +297,7 @@
                 @Override
                 public void run() {
                     for (StatusBarNotification sbn : notifications) {
-                        addNotificationInternal(sbn, currentRanking);
+                        addNotification(sbn, currentRanking);
                     }
                 }
             });
@@ -311,11 +310,30 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    if (mNotificationData.findByKey(sbn.getKey()) != null ||
-                            isHeadsUp(sbn.getKey())) {
-                        updateNotificationInternal(sbn, rankingMap);
+                    Notification n = sbn.getNotification();
+                    boolean isUpdate = mNotificationData.findByKey(sbn.getKey()) != null
+                            || isHeadsUp(sbn.getKey());
+                    boolean isGroupedChild = n.getGroup() != null
+                            && (n.flags & Notification.FLAG_GROUP_SUMMARY) == 0;
+                    if (isGroupedChild) {
+                        if (DEBUG) {
+                            Log.d(TAG, "Ignoring group child: " + sbn);
+                        }
+                        // Don't show grouped notifications. If this is an
+                        // update, i.e. the notification existed before but
+                        // wasn't a group child, remove the old instance.
+                        // Otherwise just update the ranking.
+                        if (isUpdate) {
+                            removeNotification(sbn.getKey(), rankingMap);
+                        } else {
+                            updateNotificationRanking(rankingMap);
+                        }
+                        return;
+                    }
+                    if (isUpdate) {
+                        updateNotification(sbn, rankingMap);
                     } else {
-                        addNotificationInternal(sbn, rankingMap);
+                        addNotification(sbn, rankingMap);
                     }
                 }
             });
@@ -328,7 +346,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    removeNotificationInternal(sbn.getKey(), rankingMap);
+                    removeNotification(sbn.getKey(), rankingMap);
                 }
             });
         }
@@ -339,7 +357,7 @@
             mHandler.post(new Runnable() {
                 @Override
                 public void run() {
-                    updateRankingInternal(rankingMap);
+                    updateNotificationRanking(rankingMap);
                 }
             });
         }
@@ -395,14 +413,12 @@
 
         // Connect in to the status bar manager service
         StatusBarIconList iconList = new StatusBarIconList();
-        ArrayList<StatusBarNotification> notifications = new ArrayList<StatusBarNotification>();
         mCommandQueue = new CommandQueue(this, iconList);
 
         int[] switches = new int[8];
         ArrayList<IBinder> binders = new ArrayList<IBinder>();
         try {
-            mBarService.registerStatusBar(mCommandQueue, iconList, notifications,
-                    switches, binders);
+            mBarService.registerStatusBar(mCommandQueue, iconList, switches, binders);
         } catch (RemoteException ex) {
             // If the system process isn't there we're doomed anyway.
         }
@@ -428,19 +444,12 @@
         }
 
         // Set up the initial notification state.
-        if (USE_NOTIFICATION_LISTENER) {
-            try {
-                mNotificationListener.registerAsSystemService(
-                        new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
-                        UserHandle.USER_ALL);
-            } catch (RemoteException e) {
-                Log.e(TAG, "Unable to register notification listener", e);
-            }
-        } else {
-            N = notifications.size();
-            for (int i=0; i<N; i++) {
-                addNotification(notifications.get(i));
-            }
+        try {
+            mNotificationListener.registerAsSystemService(
+                    new ComponentName(mContext.getPackageName(), getClass().getCanonicalName()),
+                    UserHandle.USER_ALL);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Unable to register notification listener", e);
         }
 
 
@@ -1175,7 +1184,7 @@
      * WARNING: this will call back into us.  Don't hold any locks.
      */
     void handleNotificationError(StatusBarNotification n, String message) {
-        removeNotification(n.getKey());
+        removeNotification(n.getKey(), null);
         try {
             mBarService.onNotificationError(n.getPackageName(), n.getTag(), n.getId(), n.getUid(),
                     n.getInitialPid(), message, n.getUserId());
@@ -1311,34 +1320,11 @@
     protected abstract void updateExpandedViewPos(int expandedPosition);
     protected abstract boolean shouldDisableNavbarGestures();
 
-    @Override
-    public void addNotification(StatusBarNotification notification) {
-        if (!USE_NOTIFICATION_LISTENER) {
-            addNotificationInternal(notification, null);
-        }
-    }
-
-    public abstract void addNotificationInternal(StatusBarNotification notification,
+    public abstract void addNotification(StatusBarNotification notification,
             RankingMap ranking);
-
-    protected abstract void updateRankingInternal(RankingMap ranking);
-
-    @Override
-    public void removeNotification(String key) {
-        if (!USE_NOTIFICATION_LISTENER) {
-            removeNotificationInternal(key, null);
-        }
-    }
-
-    public abstract void removeNotificationInternal(String key, RankingMap ranking);
-
-    public void updateNotification(StatusBarNotification notification) {
-        if (!USE_NOTIFICATION_LISTENER) {
-            updateNotificationInternal(notification, null);
-        }
-    }
-
-    public void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
+    protected abstract void updateNotificationRanking(RankingMap ranking);
+    public abstract void removeNotification(String key, RankingMap ranking);
+    public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
 
         final String key = notification.getKey();
@@ -1455,7 +1441,7 @@
                 } else {
                     if (shouldInterrupt && alertAgain) {
                         removeNotificationViews(key, ranking);
-                        addNotificationInternal(notification, ranking);  //this will pop the headsup
+                        addNotification(notification, ranking);  //this will pop the headsup
                     } else {
                         updateNotificationViews(oldEntry, notification);
                     }
@@ -1495,7 +1481,7 @@
                 if (shouldInterrupt && alertAgain) {
                     if (DEBUG) Log.d(TAG, "reposting to invoke heads up for key: " + key);
                     removeNotificationViews(key, ranking);
-                    addNotificationInternal(notification, ranking);  //this will pop the headsup
+                    addNotification(notification, ranking);  //this will pop the headsup
                 } else {
                     if (DEBUG) Log.d(TAG, "rebuilding update in place for key: " + key);
                     removeNotificationViews(key, ranking);
@@ -1653,12 +1639,10 @@
             mWindowManager.removeViewImmediate(mSearchPanelView);
         }
         mContext.unregisterReceiver(mBroadcastReceiver);
-        if (USE_NOTIFICATION_LISTENER) {
-            try {
-                mNotificationListener.unregisterAsSystemService();
-            } catch (RemoteException e) {
-                // Ignore.
-            }
+        try {
+            mNotificationListener.unregisterAsSystemService();
+        } catch (RemoteException e) {
+            // Ignore.
         }
     }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index aaeadb6..2b61f09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -41,23 +41,20 @@
     private static final int OP_REMOVE_ICON = 2;
 
     private static final int MSG_ICON                       = 1 << MSG_SHIFT;
-    private static final int MSG_ADD_NOTIFICATION           = 2 << MSG_SHIFT;
-    private static final int MSG_UPDATE_NOTIFICATION        = 3 << MSG_SHIFT;
-    private static final int MSG_REMOVE_NOTIFICATION        = 4 << MSG_SHIFT;
-    private static final int MSG_DISABLE                    = 5 << MSG_SHIFT;
-    private static final int MSG_EXPAND_NOTIFICATIONS       = 6 << MSG_SHIFT;
-    private static final int MSG_COLLAPSE_PANELS            = 7 << MSG_SHIFT;
-    private static final int MSG_EXPAND_SETTINGS            = 8 << MSG_SHIFT;
-    private static final int MSG_SET_SYSTEMUI_VISIBILITY    = 9 << MSG_SHIFT;
-    private static final int MSG_TOP_APP_WINDOW_CHANGED     = 10 << MSG_SHIFT;
-    private static final int MSG_SHOW_IME_BUTTON            = 11 << MSG_SHIFT;
-    private static final int MSG_SET_HARD_KEYBOARD_STATUS   = 12 << MSG_SHIFT;
-    private static final int MSG_TOGGLE_RECENT_APPS         = 13 << MSG_SHIFT;
-    private static final int MSG_PRELOAD_RECENT_APPS        = 14 << MSG_SHIFT;
-    private static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 15 << MSG_SHIFT;
-    private static final int MSG_SET_WINDOW_STATE           = 16 << MSG_SHIFT;
-    private static final int MSG_SHOW_RECENT_APPS           = 17 << MSG_SHIFT;
-    private static final int MSG_HIDE_RECENT_APPS           = 18 << MSG_SHIFT;
+    private static final int MSG_DISABLE                    = 2 << MSG_SHIFT;
+    private static final int MSG_EXPAND_NOTIFICATIONS       = 3 << MSG_SHIFT;
+    private static final int MSG_COLLAPSE_PANELS            = 4 << MSG_SHIFT;
+    private static final int MSG_EXPAND_SETTINGS            = 5 << MSG_SHIFT;
+    private static final int MSG_SET_SYSTEMUI_VISIBILITY    = 6 << MSG_SHIFT;
+    private static final int MSG_TOP_APP_WINDOW_CHANGED     = 7 << MSG_SHIFT;
+    private static final int MSG_SHOW_IME_BUTTON            = 8 << MSG_SHIFT;
+    private static final int MSG_SET_HARD_KEYBOARD_STATUS   = 9 << MSG_SHIFT;
+    private static final int MSG_TOGGLE_RECENT_APPS         = 10 << MSG_SHIFT;
+    private static final int MSG_PRELOAD_RECENT_APPS        = 11 << MSG_SHIFT;
+    private static final int MSG_CANCEL_PRELOAD_RECENT_APPS = 12 << MSG_SHIFT;
+    private static final int MSG_SET_WINDOW_STATE           = 13 << MSG_SHIFT;
+    private static final int MSG_SHOW_RECENT_APPS           = 14 << MSG_SHIFT;
+    private static final int MSG_HIDE_RECENT_APPS           = 15 << MSG_SHIFT;
 
     public static final int FLAG_EXCLUDE_NONE = 0;
     public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -80,9 +77,6 @@
         public void updateIcon(String slot, int index, int viewIndex,
                 StatusBarIcon old, StatusBarIcon icon);
         public void removeIcon(String slot, int index, int viewIndex);
-        public void addNotification(StatusBarNotification notification);
-        public void updateNotification(StatusBarNotification notification);
-        public void removeNotification(String key);
         public void disable(int state);
         public void animateExpandNotificationsPanel();
         public void animateCollapsePanels(int flags);
@@ -123,26 +117,6 @@
         }
     }
 
-    @Override
-    public void addNotification(StatusBarNotification notification) {
-        synchronized (mList) {
-            mHandler.obtainMessage(MSG_ADD_NOTIFICATION, 0, 0, notification).sendToTarget();
-        }
-    }
-
-    @Override
-    public void updateNotification(StatusBarNotification notification) {
-        synchronized (mList) {
-            mHandler.obtainMessage(MSG_UPDATE_NOTIFICATION, 0, 0, notification).sendToTarget();
-        }
-    }
-
-    public void removeNotification(String key) {
-        synchronized (mList) {
-            mHandler.obtainMessage(MSG_REMOVE_NOTIFICATION, 0, 0, key).sendToTarget();
-        }
-    }
-
     public void disable(int state) {
         synchronized (mList) {
             mHandler.removeMessages(MSG_DISABLE);
@@ -279,18 +253,6 @@
                     }
                     break;
                 }
-                case MSG_ADD_NOTIFICATION: {
-                    mCallbacks.addNotification((StatusBarNotification) msg.obj);
-                    break;
-                }
-                case MSG_UPDATE_NOTIFICATION: {
-                    mCallbacks.updateNotification((StatusBarNotification) msg.obj);
-                    break;
-                }
-                case MSG_REMOVE_NOTIFICATION: {
-                    mCallbacks.removeNotification((String) msg.obj);
-                    break;
-                }
                 case MSG_DISABLE:
                     mCallbacks.disable(msg.arg1);
                     break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 5bad602..5981898 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -212,13 +212,6 @@
         return mShowingPublic ? mRowMinHeight : mMaxExpandHeight;
     }
 
-    /**
-     * @return the potential height this view could expand in addition.
-     */
-    public int getExpandPotential() {
-        return getIntrinsicHeight() - getActualHeight();
-    }
-
     @Override
     public boolean isContentExpandable() {
         NotificationContentView showingLayout = getShowingLayout();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
index ac2537c..4d4a8ab 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableView.java
@@ -223,6 +223,8 @@
 
     public abstract void performAddAnimation(long delay);
 
+    public abstract void setScrimAmount(float scrimAmount);
+
     /**
      * A listener notifying when {@link #getActualHeight} changes.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java b/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
deleted file mode 100644
index bfa74fa..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/InterceptedNotifications.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright (C) 2014 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.systemui.statusbar;
-
-import android.app.Notification;
-import android.content.Context;
-import android.os.Process;
-import android.provider.Settings;
-import android.service.notification.NotificationListenerService.Ranking;
-import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.StatusBarNotification;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.view.View;
-
-import com.android.systemui.R;
-import com.android.systemui.statusbar.NotificationData.Entry;
-import com.android.systemui.statusbar.phone.PhoneStatusBar;
-
-public class InterceptedNotifications {
-    private static final String TAG = "InterceptedNotifications";
-    private static final String SYNTHETIC_KEY = "InterceptedNotifications.SYNTHETIC_KEY";
-
-    private final Context mContext;
-    private final PhoneStatusBar mBar;
-    private final ArrayMap<String, StatusBarNotification> mIntercepted
-            = new ArrayMap<String, StatusBarNotification>();
-    private final ArraySet<String> mReleased = new ArraySet<String>();
-
-    private String mSynKey;
-
-    public InterceptedNotifications(Context context, PhoneStatusBar bar) {
-        mContext = context;
-        mBar = bar;
-    }
-
-    public void releaseIntercepted() {
-        final int n = mIntercepted.size();
-        for (int i = 0; i < n; i++) {
-            final StatusBarNotification sbn = mIntercepted.valueAt(i);
-            mReleased.add(sbn.getKey());
-            mBar.displayNotification(sbn, null);
-        }
-        mIntercepted.clear();
-        updateSyntheticNotification();
-    }
-
-    public boolean tryIntercept(StatusBarNotification notification, RankingMap rankingMap) {
-        if (rankingMap == null) return false;
-        if (shouldDisplayIntercepted()) return false;
-        if (mReleased.contains(notification.getKey())) return false;
-        Ranking ranking = rankingMap.getRanking(notification.getKey());
-        if (!ranking.isInterceptedByDoNotDisturb()) return false;
-        mIntercepted.put(notification.getKey(), notification);
-        updateSyntheticNotification();
-        return true;
-    }
-
-    public void retryIntercepts(RankingMap ranking) {
-        if (ranking == null) return;
-
-        final int N = mIntercepted.size();
-        final ArraySet<String> removed = new ArraySet<String>(N);
-        for (int i = 0; i < N; i++) {
-            final StatusBarNotification sbn = mIntercepted.valueAt(i);
-            if (!tryIntercept(sbn, ranking)) {
-                removed.add(sbn.getKey());
-                mBar.displayNotification(sbn, ranking);
-            }
-        }
-        if (!removed.isEmpty()) {
-            mIntercepted.removeAll(removed);
-            updateSyntheticNotification();
-        }
-    }
-
-    public void remove(String key) {
-        if (mIntercepted.remove(key) != null) {
-            updateSyntheticNotification();
-        }
-        mReleased.remove(key);
-    }
-
-    public boolean isSyntheticEntry(Entry ent) {
-        return ent.key.equals(mSynKey);
-    }
-
-    private boolean shouldDisplayIntercepted() {
-        return Settings.Secure.getInt(mContext.getContentResolver(),
-                Settings.Secure.DISPLAY_INTERCEPTED_NOTIFICATIONS, 0) != 0;
-    }
-
-    private void updateSyntheticNotification() {
-        if (mIntercepted.isEmpty()) {
-            if (mSynKey != null) {
-                mBar.removeNotificationInternal(mSynKey, null);
-                mSynKey = null;
-            }
-            return;
-        }
-        final Notification n = new Notification.Builder(mContext)
-                .setSmallIcon(R.drawable.ic_notify_zen)
-                .setContentTitle(mContext.getResources().getQuantityString(
-                        R.plurals.zen_mode_notification_title,
-                        mIntercepted.size(), mIntercepted.size()))
-                .setContentText(mContext.getString(R.string.zen_mode_notification_text))
-                .setOngoing(true)
-                .build();
-        final StatusBarNotification sbn = new StatusBarNotification(mContext.getPackageName(),
-                mContext.getBasePackageName(),
-                TAG.hashCode(), TAG, Process.myUid(), Process.myPid(), 0, n,
-                mBar.getCurrentUserHandle());
-        if (mSynKey == null) {
-            mSynKey = sbn.getKey();
-            mBar.displayNotification(sbn, null);
-        } else {
-           mBar.updateNotificationInternal(sbn, null);
-        }
-        final NotificationData.Entry entry = mBar.mNotificationData.findByKey(mSynKey);
-        entry.row.setOnClickListener(mSynClickListener);
-    }
-
-    private final View.OnClickListener mSynClickListener = new View.OnClickListener() {
-        @Override
-        public void onClick(View v) {
-            releaseIntercepted();
-        }
-    };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 5cde979..f919501 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -17,12 +17,16 @@
 package com.android.systemui.statusbar;
 
 import android.content.Context;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
 import android.graphics.Rect;
 import android.util.AttributeSet;
-import android.view.Gravity;
 import android.view.View;
-
+import android.view.animation.Interpolator;
+import android.view.animation.LinearInterpolator;
 import android.widget.FrameLayout;
+
 import com.android.systemui.R;
 
 /**
@@ -32,6 +36,8 @@
  */
 public class NotificationContentView extends FrameLayout {
 
+    private static final long ANIMATION_DURATION_LENGTH = 170;
+
     private final Rect mClipBounds = new Rect();
 
     private View mContractedChild;
@@ -41,10 +47,17 @@
     private int mClipTopAmount;
     private int mActualHeight;
 
+    private final Interpolator mLinearInterpolator = new LinearInterpolator();
+
+    private boolean mContractedVisible = true;
+
+    private Paint mFadePaint = new Paint();
+
     public NotificationContentView(Context context, AttributeSet attrs) {
         super(context, attrs);
         mSmallHeight = getResources().getDimensionPixelSize(R.dimen.notification_min_height);
         mActualHeight = mSmallHeight;
+        mFadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.ADD));
     }
 
     @Override
@@ -60,7 +73,7 @@
         sanitizeContractedLayoutParams(child);
         addView(child);
         mContractedChild = child;
-        selectLayout();
+        selectLayout(false /* animate */, true /* force */);
     }
 
     public void setExpandedChild(View child) {
@@ -69,12 +82,12 @@
         }
         addView(child);
         mExpandedChild = child;
-        selectLayout();
+        selectLayout(false /* animate */, true /* force */);
     }
 
     public void setActualHeight(int actualHeight) {
         mActualHeight = actualHeight;
-        selectLayout();
+        selectLayout(true /* animate */, false /* force */);
         updateClipping();
     }
 
@@ -104,26 +117,60 @@
         contractedChild.setLayoutParams(lp);
     }
 
-    private void selectLayout() {
-        if (mActualHeight <= mSmallHeight || mExpandedChild == null) {
-            if (mContractedChild != null && mContractedChild.getVisibility() != View.VISIBLE) {
-                mContractedChild.setVisibility(View.VISIBLE);
-            }
-            if (mExpandedChild != null && mExpandedChild.getVisibility() != View.INVISIBLE) {
-                mExpandedChild.setVisibility(View.INVISIBLE);
-            }
-        } else {
-            if (mExpandedChild.getVisibility() != View.VISIBLE) {
-                mExpandedChild.setVisibility(View.VISIBLE);
-            }
-            if (mContractedChild != null && mContractedChild.getVisibility() != View.INVISIBLE) {
-                mContractedChild.setVisibility(View.INVISIBLE);
+    private void selectLayout(boolean animate, boolean force) {
+        if (mContractedChild == null) {
+            return;
+        }
+        boolean showContractedChild = showContractedChild();
+        if (showContractedChild != mContractedVisible || force) {
+            if (animate && mExpandedChild != null) {
+                runSwitchAnimation(showContractedChild);
+            } else if (mExpandedChild != null) {
+                mContractedChild.setVisibility(showContractedChild ? View.VISIBLE : View.INVISIBLE);
+                mContractedChild.setAlpha(showContractedChild ? 1f : 0f);
+                mExpandedChild.setVisibility(showContractedChild ? View.INVISIBLE : View.VISIBLE);
+                mExpandedChild.setAlpha(showContractedChild ? 0f : 1f);
             }
         }
+        mContractedVisible = showContractedChild;
+    }
+
+    private void runSwitchAnimation(final boolean showContractedChild) {
+        mContractedChild.setVisibility(View.VISIBLE);
+        mExpandedChild.setVisibility(View.VISIBLE);
+        mContractedChild.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
+        mExpandedChild.setLayerType(LAYER_TYPE_HARDWARE, mFadePaint);
+        setLayerType(LAYER_TYPE_HARDWARE, null);
+        mContractedChild.animate()
+                .alpha(showContractedChild ? 1f : 0f)
+                .setDuration(ANIMATION_DURATION_LENGTH)
+                .setInterpolator(mLinearInterpolator);
+        mExpandedChild.animate()
+                .alpha(showContractedChild ? 0f : 1f)
+                .setDuration(ANIMATION_DURATION_LENGTH)
+                .setInterpolator(mLinearInterpolator)
+                .withEndAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        mContractedChild.setLayerType(LAYER_TYPE_NONE, null);
+                        mExpandedChild.setLayerType(LAYER_TYPE_NONE, null);
+                        setLayerType(LAYER_TYPE_NONE, null);
+                        mContractedChild.setVisibility(showContractedChild
+                                ? View.VISIBLE
+                                : View.INVISIBLE);
+                        mExpandedChild.setVisibility(showContractedChild
+                                ? View.INVISIBLE
+                                : View.VISIBLE);
+                    }
+                });
+    }
+
+    private boolean showContractedChild() {
+        return mActualHeight <= mSmallHeight || mExpandedChild == null;
     }
 
     public void notifyContentUpdated() {
-        selectLayout();
+        selectLayout(false /* animate */, true /* force */);
     }
 
     public boolean isContentExpandable() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationScrimView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationScrimView.java
new file mode 100644
index 0000000..440b2c1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationScrimView.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar;
+
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+import com.android.keyguard.R;
+
+/**
+ * A view that can be used for both the dimmed and normal background of an notification.
+ */
+public class NotificationScrimView extends View {
+
+    private Drawable mBackground;
+    private int mClipTopAmount;
+    private int mActualHeight;
+
+    public NotificationScrimView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mBackground = getResources().getDrawable(R.drawable.notification_scrim);
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        draw(canvas, mBackground);
+    }
+
+    private void draw(Canvas canvas, Drawable drawable) {
+        if (drawable != null) {
+            drawable.setBounds(0, mClipTopAmount, getWidth(), mActualHeight);
+            drawable.draw(canvas);
+        }
+    }
+
+    @Override
+    protected boolean verifyDrawable(Drawable who) {
+        return super.verifyDrawable(who) || who == mBackground;
+    }
+
+    public void setActualHeight(int actualHeight) {
+        mActualHeight = actualHeight;
+        invalidate();
+    }
+
+    public int getActualHeight() {
+        return mActualHeight;
+    }
+
+    public void setClipTopAmount(int clipTopAmount) {
+        mClipTopAmount = clipTopAmount;
+        invalidate();
+    }
+
+    @Override
+    public boolean hasOverlappingRendering() {
+
+        // Prevents this view from creating a layer when alpha is animating.
+        return false;
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
index f80f0fd..650abaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SpeedBumpView.java
@@ -121,4 +121,9 @@
     public void performAddAnimation(long delay) {
         performVisibilityAnimation(true);
     }
+
+    @Override
+    public void setScrimAmount(float scrimAmount) {
+        // We don't need to scrim the speedbumps
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index 97aa993..63698e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -58,6 +58,7 @@
     private ImageView mCameraImageView;
     private ImageView mPhoneImageView;
     private ImageView mLockIcon;
+    private View mIndicationText;
 
     private ActivityStarter mActivityStarter;
     private UnlockMethodCache mUnlockMethodCache;
@@ -87,6 +88,7 @@
         mCameraImageView = (ImageView) findViewById(R.id.camera_button);
         mPhoneImageView = (ImageView) findViewById(R.id.phone_button);
         mLockIcon = (ImageView) findViewById(R.id.lock_icon);
+        mIndicationText = findViewById(R.id.keyguard_indication_text);
         watchForCameraPolicyChanges();
         watchForAccessibilityChanges();
         updateCameraVisibility();
@@ -231,6 +233,10 @@
         return mLockIcon;
     }
 
+    public View getIndicationView() {
+        return mIndicationText;
+    }
+
     @Override
     public void onMethodSecureChanged(boolean methodSecure) {
         updateTrust();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
index 6a83a5e..319096d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardClockPositionAlgorithm.java
@@ -18,6 +18,7 @@
 
 import android.content.res.Resources;
 import android.graphics.Path;
+import android.view.animation.AccelerateInterpolator;
 import android.view.animation.PathInterpolator;
 
 import com.android.systemui.R;
@@ -31,6 +32,10 @@
 
     private static final float CLOCK_RUBBERBAND_FACTOR_MIN = 0.08f;
     private static final float CLOCK_RUBBERBAND_FACTOR_MAX = 0.8f;
+    private static final float CLOCK_SCALE_FADE_START = 0.95f;
+    private static final float CLOCK_SCALE_FADE_END = 0.75f;
+    private static final float CLOCK_SCALE_FADE_END_NO_NOTIFS = 0.5f;
+
 
     private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MIN = 1.4f;
     private static final float CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX = 3.2f;
@@ -61,6 +66,8 @@
         sSlowDownInterpolator = new PathInterpolator(path);
     }
 
+    private AccelerateInterpolator mAccelerateInterpolator = new AccelerateInterpolator();
+
     /**
      * Refreshes the dimension values.
      */
@@ -87,18 +94,29 @@
     }
 
     public void run(Result result) {
-        int y = getClockY() - mKeyguardStatusHeight/2;
+        int y = getClockY() - mKeyguardStatusHeight / 2;
         float clockAdjustment = getClockYExpansionAdjustment();
         float topPaddingAdjMultiplier = getTopPaddingAdjMultiplier();
         result.stackScrollerPaddingAdjustment = (int) (clockAdjustment*topPaddingAdjMultiplier);
         int clockNotificationsPadding = getClockNotificationsPadding()
                 + result.stackScrollerPaddingAdjustment;
         int padding = y + clockNotificationsPadding;
-        y += clockAdjustment;
         result.clockY = y;
         result.stackScrollerPadding = mKeyguardStatusHeight + padding;
-        result.clockAlpha = getClockAlpha(result.stackScrollerPadding
-                - (y + mKeyguardStatusHeight));
+        result.clockScale = getClockScale(result.stackScrollerPadding,
+                result.clockY,
+                y + getClockNotificationsPadding() + mKeyguardStatusHeight);
+        result.clockAlpha = getClockAlpha(result.clockScale);
+    }
+
+    private float getClockScale(int notificationPadding, int clockY, int startPadding) {
+        float scaleMultiplier = getNotificationAmountT() == 0 ? 6.0f : 5.0f;
+        float scaleEnd = clockY - mKeyguardStatusHeight * scaleMultiplier;
+        float distanceToScaleEnd = notificationPadding - scaleEnd;
+        float progress = distanceToScaleEnd / (startPadding - scaleEnd);
+        progress = Math.max(0.0f, Math.min(progress, 1.0f));
+        progress = mAccelerateInterpolator.getInterpolation(progress);
+        return progress;
     }
 
     private int getClockNotificationsPadding() {
@@ -144,11 +162,12 @@
                 + t * CLOCK_ADJ_TOP_PADDING_MULTIPLIER_MAX;
     }
 
-    private float getClockAlpha(int clockNotificationPadding) {
-        float t = getNotificationAmountT();
-        t = (float) Math.pow(t, 0.3f);
-        float multiplier = 1 + 2 * (1 - t);
-        float alpha = 1 + (float) clockNotificationPadding * multiplier / mKeyguardStatusHeight * 3;
+    private float getClockAlpha(float scale) {
+        float fadeEnd = getNotificationAmountT() == 0.0f
+                ? CLOCK_SCALE_FADE_END_NO_NOTIFS
+                : CLOCK_SCALE_FADE_END;
+        float alpha = (scale - fadeEnd)
+                / (CLOCK_SCALE_FADE_START - fadeEnd);
         return Math.max(0, Math.min(1, alpha));
     }
 
@@ -168,6 +187,11 @@
         public int clockY;
 
         /**
+         * The scale of the Clock
+         */
+        public float clockScale;
+
+        /**
          * The alpha value of the clock.
          */
         public float clockAlpha;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index dde95bf..a6ce5d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -32,6 +32,7 @@
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
 import android.widget.LinearLayout;
+import android.widget.TextView;
 
 import com.android.systemui.R;
 import com.android.systemui.statusbar.ExpandableView;
@@ -48,6 +49,11 @@
         View.OnClickListener, NotificationStackScrollLayout.OnOverscrollTopChangedListener,
         KeyguardPageSwipeHelper.Callback {
 
+    // Cap and total height of Roboto font. Needs to be adjusted when font for the big clock is
+    // changed.
+    private static final int CAP_HEIGHT = 1456;
+    private static final int FONT_HEIGHT = 2163;
+
     private static final float LOCK_ICON_ACTIVE_SCALE = 1.2f;
 
     private KeyguardPageSwipeHelper mPageSwiper;
@@ -56,7 +62,7 @@
     private View mQsPanel;
     private View mKeyguardStatusView;
     private ObservableScrollView mScrollView;
-    private View mStackScrollerContainer;
+    private TextView mClockView;
 
     private NotificationStackScrollLayout mNotificationStackScroller;
     private int mNotificationTopPadding;
@@ -105,9 +111,11 @@
             new KeyguardClockPositionAlgorithm.Result();
     private boolean mIsSwipedHorizontally;
     private boolean mIsExpanding;
-    private KeyguardBottomAreaView mKeyguardBottomArea;
+
     private boolean mBlockTouches;
     private ArrayList<View> mSwipeTranslationViews = new ArrayList<>();
+    private int mNotificationScrimWaitDistance;
+    private boolean mOnNotificationsOnDown;
 
     public NotificationPanelView(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -135,9 +143,9 @@
         mHeader.getBackgroundView().setOnClickListener(this);
         mHeader.setOverlayParent(this);
         mKeyguardStatusView = findViewById(R.id.keyguard_status_view);
-        mStackScrollerContainer = findViewById(R.id.notification_container_parent);
         mQsContainer = findViewById(R.id.quick_settings_container);
         mQsPanel = findViewById(R.id.quick_settings_panel);
+        mClockView = (TextView) findViewById(R.id.clock_view);
         mScrollView = (ObservableScrollView) findViewById(R.id.scroll_view);
         mScrollView.setListener(this);
         mNotificationStackScroller = (NotificationStackScrollLayout)
@@ -169,12 +177,19 @@
                 getResources().getDimensionPixelSize(R.dimen.header_notifications_collide_distance);
         mUnlockMoveDistance = getResources().getDimensionPixelOffset(R.dimen.unlock_move_distance);
         mClockPositionAlgorithm.loadDimens(getResources());
+        mNotificationScrimWaitDistance =
+                getResources().getDimensionPixelSize(R.dimen.notification_scrim_wait_distance);
     }
 
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
 
+        // Update Clock Pivot
+        mKeyguardStatusView.setPivotX(getWidth() / 2);
+        mKeyguardStatusView.setPivotY(
+                (FONT_HEIGHT - CAP_HEIGHT) / 2048f * mClockView.getTextSize());
+
         // Calculate quick setting heights.
         mQsMinExpansionHeight = mHeader.getCollapsedHeight() + mQsPeekHeight;
         mQsMaxExpansionHeight = mHeader.getExpandedHeight() + mQsContainer.getHeight();
@@ -197,7 +212,7 @@
      * showing.
      */
     private void positionClockAndNotifications() {
-        boolean animateClock = mNotificationStackScroller.isAddOrRemoveAnimationPending();
+        boolean animate = mNotificationStackScroller.isAddOrRemoveAnimationPending();
         int stackScrollerPadding;
         if (mStatusBar.getBarState() != StatusBarState.KEYGUARD) {
             int bottom = mStackScrollerOverscrolling
@@ -215,17 +230,17 @@
                     getHeight(),
                     mKeyguardStatusView.getHeight());
             mClockPositionAlgorithm.run(mClockPositionResult);
-            if (animateClock || mClockAnimator != null) {
+            if (animate || mClockAnimator != null) {
                 startClockAnimation(mClockPositionResult.clockY);
             } else {
                 mKeyguardStatusView.setY(mClockPositionResult.clockY);
             }
-            applyClockAlpha(mClockPositionResult.clockAlpha);
+            updateClock(mClockPositionResult.clockAlpha, mClockPositionResult.clockScale);
             stackScrollerPadding = mClockPositionResult.stackScrollerPadding;
             mTopPaddingAdjustment = mClockPositionResult.stackScrollerPaddingAdjustment;
         }
         mNotificationStackScroller.setIntrinsicPadding(stackScrollerPadding);
-        requestScrollerTopPaddingUpdate(animateClock);
+        requestScrollerTopPaddingUpdate(animate);
     }
 
     private void startClockAnimation(int y) {
@@ -258,13 +273,10 @@
         });
     }
 
-    private void applyClockAlpha(float alpha) {
-        if (alpha != 1.0f) {
-            mKeyguardStatusView.setLayerType(LAYER_TYPE_HARDWARE, null);
-        } else {
-            mKeyguardStatusView.setLayerType(LAYER_TYPE_NONE, null);
-        }
+    private void updateClock(float alpha, float scale) {
         mKeyguardStatusView.setAlpha(alpha);
+        mKeyguardStatusView.setScaleX(scale);
+        mKeyguardStatusView.setScaleY(scale);
     }
 
     public void animateToFullShade() {
@@ -344,6 +356,7 @@
                 mInitialTouchX = x;
                 initVelocityTracker();
                 trackMovement(event);
+                mOnNotificationsOnDown = isOnNotifications(x, y);
                 if (shouldQuickSettingsIntercept(mInitialTouchX, mInitialTouchY, 0)) {
                     getParent().requestDisallowInterceptTouchEvent(true);
                 }
@@ -391,6 +404,8 @@
                 if (mQsTracking) {
                     flingQsWithCurrentVelocity();
                     mQsTracking = false;
+                } else if (mQsFullyExpanded && mOnNotificationsOnDown) {
+                    flingSettings(0 /* vel */, false /* expand */);
                 }
                 mIntercepting = false;
                 break;
@@ -398,6 +413,10 @@
         return !mQsExpanded && super.onInterceptTouchEvent(event);
     }
 
+    private boolean isOnNotifications(float x, float y) {
+        return mNotificationStackScroller.getChildAtPosition(x, y) != null;
+    }
+
     @Override
     public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
 
@@ -577,9 +596,17 @@
         mHeader.setExpansion(height - mQsPeekHeight);
         setQsTranslation(height);
         requestScrollerTopPaddingUpdate(false /* animate */);
+        updateNotificationScrim(height);
         mStatusBar.userActivity();
     }
 
+    private void updateNotificationScrim(float height) {
+        int startDistance = mQsMinExpansionHeight + mNotificationScrimWaitDistance;
+        float progress = (height - startDistance) / (mQsMaxExpansionHeight - startDistance);
+        progress = Math.max(0.0f, Math.min(progress, 1.0f));
+        mNotificationStackScroller.setScrimAlpha(progress);
+    }
+
     private void setQsTranslation(float height) {
         mQsContainer.setY(height - mQsContainer.getHeight());
     }
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 1f3098d..12aa004 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -62,6 +62,7 @@
     protected int mTouchSlop;
     protected boolean mHintAnimationRunning;
     private boolean mOverExpandedBeforeFling;
+    private float mOriginalIndicationY;
 
     private ValueAnimator mHeightAnimator;
     private ObjectAnimator mPeekAnimator;
@@ -82,6 +83,7 @@
 
     private Interpolator mLinearOutSlowInInterpolator;
     private Interpolator mBounceInterpolator;
+    protected KeyguardBottomAreaView mKeyguardBottomArea;
 
     protected void onExpandingFinished() {
         mBar.onExpandingFinished();
@@ -652,6 +654,22 @@
         });
         animator.start();
         mHeightAnimator = animator;
+        mOriginalIndicationY = mKeyguardBottomArea.getIndicationView().getY();
+        mKeyguardBottomArea.getIndicationView().animate()
+                .y(mOriginalIndicationY - mHintDistance)
+                .setDuration(250)
+                .setInterpolator(mLinearOutSlowInInterpolator)
+                .withEndAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        mKeyguardBottomArea.getIndicationView().animate()
+                                .y(mOriginalIndicationY)
+                                .setDuration(450)
+                                .setInterpolator(mBounceInterpolator)
+                                .start();
+                    }
+                })
+                .start();
     }
 
     /**
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 1da7dab..00951b2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -35,6 +35,7 @@
 import android.animation.ValueAnimator;
 import android.app.ActivityManager;
 import android.app.ActivityManagerNative;
+import android.app.IActivityManager;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
@@ -64,6 +65,7 @@
 import android.os.UserHandle;
 import android.provider.Settings;
 import android.provider.Settings.Global;
+import android.provider.Settings.SettingNotFoundException;
 import android.service.notification.NotificationListenerService.RankingMap;
 import android.service.notification.StatusBarNotification;
 import android.util.ArraySet;
@@ -82,7 +84,6 @@
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewPropertyAnimator;
 import android.view.ViewStub;
-import android.view.ViewTreeObserver;
 import android.view.WindowManager;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.Animation;
@@ -109,7 +110,6 @@
 import com.android.systemui.statusbar.DragDownHelper;
 import com.android.systemui.statusbar.ExpandableNotificationRow;
 import com.android.systemui.statusbar.GestureRecorder;
-import com.android.systemui.statusbar.InterceptedNotifications;
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
@@ -123,6 +123,7 @@
 import com.android.systemui.statusbar.policy.CastControllerImpl;
 import com.android.systemui.statusbar.policy.DateView;
 import com.android.systemui.statusbar.policy.HeadsUpNotificationView;
+import com.android.systemui.statusbar.policy.KeyguardUserSwitcher;
 import com.android.systemui.statusbar.policy.UserInfoController;
 import com.android.systemui.statusbar.policy.LocationControllerImpl;
 import com.android.systemui.statusbar.policy.NetworkControllerImpl;
@@ -211,6 +212,7 @@
     ZenModeController mZenModeController;
     CastControllerImpl mCastController;
     VolumeComponent mVolumeComponent;
+    KeyguardUserSwitcher mKeyguardUserSwitcher;
 
     int mNaturalBarHeight = -1;
     int mIconSize = -1;
@@ -260,6 +262,10 @@
     boolean mLeaveOpenOnKeyguardHide;
     KeyguardIndicationController mKeyguardIndicationController;
 
+    private boolean mKeyguardFadingAway;
+    private long mKeyguardFadingAwayDelay;
+    private long mKeyguardFadingAwayDuration;
+
     int mKeyguardMaxNotificationCount;
     View mDateTimeView;
 
@@ -392,14 +398,15 @@
         }};
 
     private Runnable mOnFlipRunnable;
-    private InterceptedNotifications mIntercepted;
     private VelocityTracker mSettingsTracker;
     private float mSettingsDownY;
     private boolean mSettingsStarted;
     private boolean mSettingsCancelled;
     private boolean mSettingsClosing;
     private boolean mVisible;
+    private boolean mWaitingForKeyguardExit;
 
+    private Interpolator mLinearOutSlowIn;
     private Interpolator mAlphaOut = new PathInterpolator(0f, 0.4f, 1f, 1f);
     private Interpolator mAlphaIn = new PathInterpolator(0f, 0f, 0.8f, 1f);
 
@@ -501,19 +508,6 @@
     };
 
     @Override
-    public void setZenMode(int mode) {
-        super.setZenMode(mode);
-        if (!isDeviceProvisioned()) return;
-        final boolean zen = mode != Settings.Global.ZEN_MODE_OFF;
-        if (!zen) {
-            mIntercepted.releaseIntercepted();
-        }
-        if (mIconPolicy != null) {
-            mIconPolicy.setZenMode(zen);
-        }
-    }
-
-    @Override
     protected void setShowLockscreenNotifications(boolean show) {
         super.setShowLockscreenNotifications(show);
         updateStackScrollerState();
@@ -524,7 +518,6 @@
         mDisplay = ((WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE))
                 .getDefaultDisplay();
         updateDisplaySize();
-        mIntercepted = new InterceptedNotifications(mContext, this);
         super.start(); // calls createAndAddWindows()
 
         addNavigationBar();
@@ -713,6 +706,8 @@
         final SignalClusterView signalCluster =
                 (SignalClusterView)mStatusBarView.findViewById(R.id.signal_cluster);
 
+        mKeyguardUserSwitcher = new KeyguardUserSwitcher(mContext,
+                (ViewStub) mStatusBarWindow.findViewById(R.id.keyguard_user_switcher), mHeader);
 
         mNetworkController.addSignalCluster(signalCluster);
         signalCluster.setNetworkController(mNetworkController);
@@ -893,6 +888,14 @@
         }
     };
 
+    private View.OnLongClickListener mLockToAppClickListener = new View.OnLongClickListener() {
+        @Override
+        public boolean onLongClick(View v) {
+            toggleLockedApp();
+            return true;
+        }
+    };
+
     private int mShowSearchHoldoff = 0;
     private Runnable mShowSearchPanel = new Runnable() {
         public void run() {
@@ -936,6 +939,8 @@
 
         mNavigationBarView.getRecentsButton().setOnClickListener(mRecentsClickListener);
         mNavigationBarView.getRecentsButton().setOnTouchListener(mRecentsPreloadOnTouchListener);
+        mNavigationBarView.getRecentsButton().setLongClickable(true);
+        mNavigationBarView.getRecentsButton().setOnLongClickListener(mLockToAppClickListener);
         mNavigationBarView.getHomeButton().setOnTouchListener(mHomeActionListener);
         updateSearchPanel();
     }
@@ -1051,18 +1056,8 @@
     }
 
     @Override
-    public void addNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
+    public void addNotification(StatusBarNotification notification, RankingMap ranking) {
         if (DEBUG) Log.d(TAG, "addNotification key=" + notification.getKey());
-        if (mZenMode != Global.ZEN_MODE_OFF && mIntercepted.tryIntercept(notification, ranking)) {
-            // Forward the ranking so we can sort the new notification.
-            mNotificationData.updateRanking(ranking);
-            return;
-        }
-        mIntercepted.remove(notification.getKey());
-        displayNotification(notification, ranking);
-    }
-
-    public void displayNotification(StatusBarNotification notification, RankingMap ranking) {
         if (mUseHeadsUp && shouldInterrupt(notification)) {
             if (DEBUG) Log.d(TAG, "launching notification in heads up mode");
             Entry interruptionCandidate = new Entry(notification, null);
@@ -1144,21 +1139,13 @@
     }
 
     @Override
-    public void updateNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
-        super.updateNotificationInternal(notification, ranking);
-        // if we're here, then the notification is already in the shade
-        mIntercepted.remove(notification.getKey());
-    }
-
-    @Override
-    protected void updateRankingInternal(RankingMap ranking) {
+    protected void updateNotificationRanking(RankingMap ranking) {
         mNotificationData.updateRanking(ranking);
-        mIntercepted.retryIntercepts(ranking);
         updateNotifications();
     }
 
     @Override
-    public void removeNotificationInternal(String key, RankingMap ranking) {
+    public void removeNotification(String key, RankingMap ranking) {
         if (ENABLE_HEADS_UP && mHeadsUpNotificationView.getEntry() != null
                 && key.equals(mHeadsUpNotificationView.getEntry().notification.getKey())) {
             mHeadsUpNotificationView.clear();
@@ -1181,7 +1168,6 @@
                 animateCollapsePanels();
             }
         }
-        mIntercepted.remove(key);
         setAreThereNotifications();
     }
 
@@ -1337,9 +1323,6 @@
                 // in "public" mode (atop a secure keyguard), secret notifs are totally hidden
                 continue;
             }
-            if (mIntercepted.isSyntheticEntry(ent)) {
-                continue;
-            }
             toShow.add(ent.icon);
         }
 
@@ -1453,7 +1436,7 @@
     }
 
     private int adjustDisableFlags(int state) {
-        if (mExpandedVisible) {
+        if (mExpandedVisible || mBouncerShowing || mWaitingForKeyguardExit) {
             state |= StatusBarManager.DISABLE_NOTIFICATION_ICONS;
             state |= StatusBarManager.DISABLE_SYSTEM_INFO;
         }
@@ -1501,19 +1484,9 @@
         if ((diff & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
             mSystemIconArea.animate().cancel();
             if ((state & StatusBarManager.DISABLE_SYSTEM_INFO) != 0) {
-                mSystemIconArea.animate()
-                    .alpha(0f)
-                    .withLayer()
-                    .setDuration(160)
-                    .setInterpolator(mAlphaIn)
-                    .setListener(mMakeIconsInvisible);
+                animateStatusBarHide(mSystemIconArea);
             } else {
-                mSystemIconArea.setVisibility(View.VISIBLE);
-                mSystemIconArea.animate()
-                    .alpha(1f)
-                    .withLayer()
-                    .setInterpolator(mAlphaOut)
-                    .setDuration(320);
+                animateStatusBarShow(mSystemIconArea);
             }
         }
 
@@ -1546,25 +1519,48 @@
                 if (mTicking) {
                     haltTicker();
                 }
-
-                mNotificationIcons.animate()
-                    .alpha(0f)
-                    .withLayer()
-                    .setDuration(160)
-                    .setInterpolator(mAlphaIn)
-                    .setListener(mMakeIconsInvisible)
-                    .start();
+                animateStatusBarHide(mNotificationIcons);
             } else {
-                mNotificationIcons.setVisibility(View.VISIBLE);
-                mNotificationIcons.animate()
-                    .alpha(1f)
-                    .withLayer()
-                    .setInterpolator(mAlphaOut)
-                    .setDuration(320);
+                animateStatusBarShow(mNotificationIcons);
             }
         }
     }
 
+    /**
+     * Animates {@code v}, a view that is part of the status bar, out.
+     */
+    private void animateStatusBarHide(View v) {
+        v.animate()
+                .alpha(0f)
+                .withLayer()
+                .setDuration(160)
+                .setInterpolator(mAlphaIn)
+                .setStartDelay(0)
+                .setListener(mMakeIconsInvisible)
+                .start();
+    }
+
+    /**
+     * Animates {@code v}, a view that is part of the status bar, in.
+     */
+    private void animateStatusBarShow(View v) {
+        v.setVisibility(View.VISIBLE);
+        v.animate()
+                .alpha(1f)
+                .withLayer()
+                .setInterpolator(mAlphaOut)
+                .setDuration(320)
+                .setStartDelay(0);
+
+        // Synchronize the motion with the Keyguard fading if necessary.
+        if (mKeyguardFadingAway) {
+            v.animate()
+                    .setDuration(mKeyguardFadingAwayDuration)
+                    .setInterpolator(mLinearOutSlowIn)
+                    .setStartDelay(mKeyguardFadingAwayDelay);
+        }
+    }
+
     @Override
     protected BaseStatusBar.H createHandler() {
         return new PhoneStatusBar.H();
@@ -1666,6 +1662,7 @@
         mStatusBarWindowManager.setStatusBarExpanded(true);
 
         visibilityChanged(true);
+        mWaitingForKeyguardExit = false;
         disable(mDisabledUnmodified);
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, true);
     }
@@ -1859,8 +1856,8 @@
         }
 
         setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
-        disable(mDisabledUnmodified);
         showBouncer();
+        disable(mDisabledUnmodified);
     }
 
     public boolean interceptTouchEvent(MotionEvent event) {
@@ -2574,6 +2571,8 @@
         if (mQSPanel != null) mQSPanel.updateResources();
 
         loadDimens();
+        mLinearOutSlowIn = AnimationUtils.loadInterpolator(
+                mContext, android.R.interpolator.linear_out_slow_in);
     }
 
     protected void loadDimens() {
@@ -2901,6 +2900,27 @@
         updateKeyguardState();
     }
 
+    /**
+     * Notifies the status bar the Keyguard is fading away with the specified timings.
+     *
+     * @param delay the animation delay in miliseconds
+     * @param fadeoutDuration the duration of the exit animation, in milliseconds
+     */
+    public void setKeyguardFadingAway(long delay, long fadeoutDuration) {
+        mKeyguardFadingAway = true;
+        mKeyguardFadingAwayDelay = delay;
+        mKeyguardFadingAwayDuration = fadeoutDuration;
+        mWaitingForKeyguardExit = false;
+        disable(mDisabledUnmodified);
+    }
+
+    /**
+     * Notifies that the Keyguard fading away animation is done.
+     */
+    public void finishKeyguardFadingAway() {
+        mKeyguardFadingAway = false;
+    }
+
     private void updatePublicMode() {
         setLockscreenPublicMode(
                 (mStatusBarKeyguardViewManager.isShowing() || 
@@ -2913,9 +2933,11 @@
             mKeyguardStatusView.setVisibility(View.VISIBLE);
             mKeyguardIndicationController.setVisible(true);
             mNotificationPanel.resetViews();
+            mKeyguardUserSwitcher.setKeyguard(true);
         } else {
             mKeyguardStatusView.setVisibility(View.GONE);
             mKeyguardIndicationController.setVisible(false);
+            mKeyguardUserSwitcher.setKeyguard(false);
         }
         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
             mKeyguardBottomArea.setVisibility(View.VISIBLE);
@@ -2975,6 +2997,7 @@
 
     private void showBouncer() {
         if (mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED) {
+            mWaitingForKeyguardExit = true;
             mStatusBarKeyguardViewManager.dismiss();
         }
     }
@@ -3125,6 +3148,12 @@
         mSystemIconArea.addView(mSystemIcons, 0);
     }
 
+    @Override
+    public void setBouncerShowing(boolean bouncerShowing) {
+        super.setBouncerShowing(bouncerShowing);
+        disable(mDisabledUnmodified);
+    }
+
     public void onScreenTurnedOff() {
         mStackScroller.setAnimationsEnabled(false);
     }
@@ -3133,6 +3162,28 @@
         mStackScroller.setAnimationsEnabled(true);
     }
 
+    public void toggleLockedApp() {
+        Log.d(TAG, "Trying to toggle lock-to-app");
+        try {
+            IActivityManager activityManager = ActivityManagerNative.getDefault();
+            if (activityManager.isInLockTaskMode()) {
+                activityManager.stopLockTaskModeOnCurrent();
+            } else {
+                try {
+                    boolean lockToAppEnabled = Settings.System.getInt(mContext.getContentResolver(),
+                            Settings.System.LOCK_TO_APP_ENABLED) != 0;
+                    if (lockToAppEnabled) {
+                        activityManager.startLockTaskModeOnCurrent();
+                    }
+                } catch (SettingNotFoundException e) {
+                    // No setting, not enabled.
+                }
+            }
+        } catch (RemoteException e) {
+            Log.d(TAG, "Unable to toggle Lock-to-app", e);
+        }
+    }
+
     private final Runnable mUserActivity = new Runnable() {
         @Override
         public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
index 1712124..fc10a08 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarHeaderView.java
@@ -63,6 +63,7 @@
 
     private boolean mShowEmergencyCallsOnly;
     private boolean mShowChargingInfo;
+    private boolean mKeyguardUserSwitcherShowing;
 
     private int mCollapsedHeight;
     private int mExpandedHeight;
@@ -72,6 +73,7 @@
     private int mNormalWidth;
     private int mPadding;
     private int mMultiUserExpandedMargin;
+    private int mSystemIconsSwitcherHiddenExpandedMargin;
 
     private ActivityStarter mActivityStarter;
     private BrightnessController mBrightnessController;
@@ -125,7 +127,8 @@
         mPadding = getResources().getDimensionPixelSize(R.dimen.notification_side_padding);
         mMultiUserExpandedMargin =
                 getResources().getDimensionPixelSize(R.dimen.multi_user_switch_expanded_margin);
-
+        mSystemIconsSwitcherHiddenExpandedMargin = getResources().getDimensionPixelSize(
+                R.dimen.system_icons_switcher_hidden_expanded_margin);
     }
 
     public void setActivityStarter(ActivityStarter activityStarter) {
@@ -216,12 +219,15 @@
                 ? VISIBLE : GONE);
         mChargingInfo.setVisibility(mExpanded && !mOverscrolled && mShowChargingInfo
                 && !mShowEmergencyCallsOnly ? VISIBLE : GONE);
+        mMultiUserSwitch.setVisibility(mExpanded || !mKeyguardUserSwitcherShowing
+                ? VISIBLE : GONE);
     }
 
     private void updateSystemIconsLayoutParams() {
         RelativeLayout.LayoutParams lp = (LayoutParams) mSystemIconsContainer.getLayoutParams();
         boolean systemIconsAboveClock = mExpanded && !mOverscrolled
                 && mShowChargingInfo && !mShowEmergencyCallsOnly;
+        lp.setMarginEnd(0);
         if (systemIconsAboveClock) {
             lp.addRule(ALIGN_PARENT_START);
             lp.removeRule(START_OF);
@@ -230,7 +236,11 @@
                     ? mSettingsButton.getId()
                     : mMultiUserSwitch.getId());
             lp.removeRule(ALIGN_PARENT_START);
+            if (mMultiUserSwitch.getVisibility() == GONE) {
+                lp.setMarginEnd(mSystemIconsSwitcherHiddenExpandedMargin);
+            }
         }
+        mSystemIconsContainer.setLayoutParams(lp);
 
         RelativeLayout.LayoutParams clockLp = (LayoutParams) mDateTime.getLayoutParams();
         if (systemIconsAboveClock) {
@@ -238,6 +248,7 @@
         } else {
             clockLp.addRule(BELOW, mEmergencyCallsOnly.getId());
         }
+        mDateTime.setLayoutParams(clockLp);
     }
 
     private void updateBrightnessControllerState() {
@@ -385,4 +396,11 @@
     public void setChargingInfo(CharSequence chargingInfo) {
         mChargingInfo.setText(chargingInfo);
     }
+
+    public void setKeyguardUserSwitcherShowing(boolean showing) {
+        // STOPSHIP: NOT CALLED PROPERLY WHEN GOING TO FULL SHADE AND RETURNING!?!
+        mKeyguardUserSwitcherShowing = showing;
+        updateVisibilities();
+        updateSystemIconsLayoutParams();
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index 09e4d94..a36f3d6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -190,19 +190,23 @@
      */
     public void hide(long startTime, long fadeoutDuration) {
         mShowing = false;
-        mPhoneStatusBar.hideKeyguard();
-        mStatusBarWindowManager.setKeyguardFadingAway(true);
-        mStatusBarWindowManager.setKeyguardShowing(false);
+
         long uptimeMillis = SystemClock.uptimeMillis();
         long delay = startTime - uptimeMillis;
         if (delay < 0) {
             delay = 0;
         }
+
+        mPhoneStatusBar.setKeyguardFadingAway(delay, fadeoutDuration);
+        mPhoneStatusBar.hideKeyguard();
+        mStatusBarWindowManager.setKeyguardFadingAway(true);
+        mStatusBarWindowManager.setKeyguardShowing(false);
         mBouncer.animateHide(delay, fadeoutDuration);
         mScrimController.animateKeyguardFadingOut(delay, fadeoutDuration, new Runnable() {
             @Override
             public void run() {
                 mStatusBarWindowManager.setKeyguardFadingAway(false);
+                mPhoneStatusBar.finishKeyguardFadingAway();
             }
         });
         mViewMediatorCallback.keyguardGone();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
new file mode 100644
index 0000000..6f2642a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UserAvatarView.java
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar.phone;
+
+import com.android.systemui.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.view.View;
+
+/**
+ * A view that displays a user image cropped to a circle with a frame.
+ */
+public class UserAvatarView extends View {
+
+    private int mActiveFrameColor;
+    private int mFrameColor;
+    private float mFrameWidth;
+    private Bitmap mBitmap;
+    private Drawable mDrawable;
+
+    private final Paint mFramePaint = new Paint();
+    private final Paint mBitmapPaint = new Paint();
+    private final Matrix mDrawMatrix = new Matrix();
+
+    private float mScale = 1;
+
+    public UserAvatarView(Context context, AttributeSet attrs,
+            int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+        final TypedArray a = context.obtainStyledAttributes(
+                attrs, R.styleable.UserAvatarView, defStyleAttr, defStyleRes);
+        final int N = a.getIndexCount();
+        for (int i = 0; i < N; i++) {
+            int attr = a.getIndex(i);
+            switch (attr) {
+                case R.styleable.UserAvatarView_frameWidth:
+                    setFrameWidth(a.getDimension(attr, 0));
+                    break;
+                case R.styleable.UserAvatarView_activeFrameColor:
+                    setActiveFrameColor(a.getColor(attr, 0));
+                    break;
+                case R.styleable.UserAvatarView_frameColor:
+                    setFrameColor(a.getColor(attr, 0));
+                    break;
+            }
+        }
+        a.recycle();
+
+        mFramePaint.setAntiAlias(true);
+        mFramePaint.setStyle(Paint.Style.STROKE);
+        mBitmapPaint.setAntiAlias(true);
+    }
+
+    public UserAvatarView(Context context, AttributeSet attrs, int defStyleAttr) {
+        this(context, attrs, defStyleAttr, 0);
+    }
+
+    public UserAvatarView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public UserAvatarView(Context context) {
+        this(context, null);
+    }
+
+    public void setBitmap(Bitmap bitmap) {
+        setDrawable(null);
+        mBitmap = bitmap;
+        if (mBitmap != null) {
+            mBitmapPaint.setShader(new BitmapShader(
+                    bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
+        } else {
+            mBitmapPaint.setShader(null);
+        }
+        configureBounds();
+        invalidate();
+    }
+
+    public void setFrameColor(int frameColor) {
+        mFrameColor = frameColor;
+        invalidate();
+    }
+
+    public void setActiveFrameColor(int activeFrameColor) {
+        mActiveFrameColor = activeFrameColor;
+        invalidate();
+    }
+
+    public void setFrameWidth(float frameWidth) {
+        mFrameWidth = frameWidth;
+        invalidate();
+    }
+
+    @Override
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+        super.onLayout(changed, left, top, right, bottom);
+        configureBounds();
+    }
+
+    public void configureBounds() {
+        int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
+        int vheight = getHeight() - mPaddingTop - mPaddingBottom;
+
+        int dwidth;
+        int dheight;
+        if (mBitmap != null) {
+            dwidth = mBitmap.getWidth();
+            dheight = mBitmap.getHeight();
+        } else if (mDrawable != null) {
+            dwidth = mDrawable.getIntrinsicWidth();
+            dheight = mDrawable.getIntrinsicHeight();
+            mDrawable.setBounds(0, 0, dwidth, dheight);
+            vwidth -= 2 * (mFrameWidth - 1);
+            vheight -= 2 * (mFrameWidth - 1);
+        } else {
+            return;
+        }
+
+        float scale;
+        float dx;
+        float dy;
+
+        scale = Math.min((float) vwidth / (float) dwidth,
+                (float) vheight / (float) dheight);
+
+        dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
+        dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);
+
+        mDrawMatrix.setScale(scale, scale);
+        mDrawMatrix.postTranslate(dx, dy);
+        mScale = scale;
+    }
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        int frameColor = isActivated() ? mActiveFrameColor : mFrameColor;
+        float halfW = getWidth() / 2f;
+        float halfH = getHeight() / 2f;
+        float halfSW = Math.min(halfH, halfW);
+        if (mBitmap != null && mScale > 0) {
+            int saveCount = canvas.getSaveCount();
+            canvas.save();
+            canvas.translate(mPaddingLeft, mPaddingTop);
+            canvas.concat(mDrawMatrix);
+            float halfBW = mBitmap.getWidth() / 2f;
+            float halfBH = mBitmap.getHeight() / 2f;
+            float halfBSW = Math.min(halfBH, halfBW);
+            canvas.drawCircle(halfBW, halfBH, halfBSW - mFrameWidth / mScale + 1, mBitmapPaint);
+            canvas.restoreToCount(saveCount);
+        } else if (mDrawable != null && mScale > 0) {
+            int saveCount = canvas.getSaveCount();
+            canvas.save();
+            canvas.translate(mPaddingLeft, mPaddingTop);
+            canvas.translate(mFrameWidth - 1, mFrameWidth - 1);
+            canvas.concat(mDrawMatrix);
+            mDrawable.draw(canvas);
+            canvas.restoreToCount(saveCount);
+        }
+        if (frameColor != 0) {
+            mFramePaint.setColor(frameColor);
+            mFramePaint.setStrokeWidth(mFrameWidth);
+            canvas.drawCircle(halfW, halfH, halfSW - mFrameWidth / 2f, mFramePaint);
+        }
+    }
+
+    public void setDrawable(Drawable d) {
+        if (mDrawable != null) {
+            mDrawable.setCallback(null);
+            unscheduleDrawable(mDrawable);
+        }
+        mDrawable = d;
+        if (d != null) {
+            d.setCallback(this);
+            if (d.isStateful()) {
+                d.setState(getDrawableState());
+            }
+            d.setLayoutDirection(getLayoutDirection());
+            configureBounds();
+        }
+        if (d != null) {
+            mBitmap = null;
+        }
+        configureBounds();
+        invalidate();
+    }
+
+    @Override
+    public void invalidateDrawable(Drawable dr) {
+        if (dr == mDrawable) {
+            invalidate();
+        } else {
+            super.invalidateDrawable(dr);
+        }
+    }
+}
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 330b599..0134fe8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -63,6 +63,7 @@
                     // Just an old-fashioned ImageView
                     performLongClick();
                 }
+                setPressed(false);
             }
         }
     };
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
new file mode 100644
index 0000000..c90750c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyguardUserSwitcher.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2014 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.systemui.statusbar.policy;
+
+import com.android.systemui.R;
+import com.android.systemui.statusbar.phone.StatusBarHeaderView;
+import com.android.systemui.statusbar.phone.UserAvatarView;
+
+import android.app.ActivityManagerNative;
+import android.content.Context;
+import android.content.pm.UserInfo;
+import android.graphics.Bitmap;
+import android.os.AsyncTask;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewStub;
+import android.view.WindowManagerGlobal;
+import android.widget.TextView;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Manages the user switcher on the Keyguard.
+ */
+public class KeyguardUserSwitcher implements View.OnClickListener {
+
+    private static final String TAG = "KeyguardUserSwitcher";
+
+    private final Context mContext;
+    private final ViewGroup mUserSwitcher;
+    private final UserManager mUserManager;
+    private final StatusBarHeaderView mHeader;
+
+    public KeyguardUserSwitcher(Context context, ViewStub userSwitcher,
+            StatusBarHeaderView header) {
+        mContext = context;
+        if (context.getResources().getBoolean(R.bool.config_keyguardUserSwitcher)) {
+            mUserSwitcher = (ViewGroup) userSwitcher.inflate();
+            mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
+            mHeader = header;
+            refresh();
+        } else {
+            mUserSwitcher = null;
+            mUserManager = null;
+            mHeader = null;
+        }
+    }
+
+    public void setKeyguard(boolean keyguard) {
+        if (mUserSwitcher != null) {
+            // TODO: Cache showUserSwitcherOnKeyguard().
+            if (keyguard && showUserSwitcherOnKeyguard()) {
+                show();
+                refresh();
+            } else {
+                hide();
+            }
+        }
+    }
+
+    /**
+     * @return true if the user switcher should be shown on the lock screen.
+     * @see android.os.UserManager#isUserSwitcherEnabled()
+     */
+    private boolean showUserSwitcherOnKeyguard() {
+        // TODO: Set isEdu. The edu provisioning process can add settings to Settings.Global.
+        boolean isEdu = false;
+        if (isEdu) {
+            return true;
+        }
+        List<UserInfo> users = mUserManager.getUsers(true /* excludeDying */);
+        int N = users.size();
+        int switchableUsers = 0;
+        for (int i = 0; i < N; i++) {
+            if (users.get(i).supportsSwitchTo()) {
+                switchableUsers++;
+            }
+        }
+        return switchableUsers > 1;
+    }
+
+    public void show() {
+        if (mUserSwitcher != null) {
+            // TODO: animate
+            mUserSwitcher.setVisibility(View.VISIBLE);
+            mHeader.setKeyguardUserSwitcherShowing(true);
+        }
+    }
+
+    private void hide() {
+        if (mUserSwitcher != null) {
+            // TODO: animate
+            mUserSwitcher.setVisibility(View.GONE);
+            mHeader.setKeyguardUserSwitcherShowing(false);
+        }
+    }
+
+    private void refresh() {
+        if (mUserSwitcher != null) {
+            new AsyncTask<Void, Void, ArrayList<UserData>>() {
+                @Override
+                protected ArrayList<UserData> doInBackground(Void... params) {
+                    return loadUsers();
+                }
+
+                @Override
+                protected void onPostExecute(ArrayList<UserData> userInfos) {
+                    bind(userInfos);
+                }
+            }.execute((Void[]) null);
+        }
+    }
+
+    private void bind(ArrayList<UserData> userList) {
+        mUserSwitcher.removeAllViews();
+        int N = userList.size();
+        for (int i = 0; i < N; i++) {
+            mUserSwitcher.addView(inflateUser(userList.get(i)));
+        }
+        // TODO: add Guest
+        // TODO: add (+) button
+    }
+
+    private View inflateUser(UserData user) {
+        View v = LayoutInflater.from(mUserSwitcher.getContext()).inflate(
+                R.layout.keyguard_user_switcher_item, mUserSwitcher, false);
+        TextView name = (TextView) v.findViewById(R.id.name);
+        UserAvatarView picture = (UserAvatarView) v.findViewById(R.id.picture);
+        name.setText(user.userInfo.name);
+        picture.setActivated(user.isCurrent);
+        if (user.userInfo.isGuest()) {
+            picture.setDrawable(mContext.getResources().getDrawable(R.drawable.ic_account_circle));
+        } else {
+            picture.setBitmap(user.userIcon);
+        }
+        v.setOnClickListener(this);
+        v.setTag(user.userInfo);
+        // TODO: mark which user is current for accessibility.
+        return v;
+    }
+
+    @Override
+    public void onClick(View v) {
+        switchUser(((UserInfo)v.getTag()).id);
+    }
+
+    // TODO: Factor out logic below and share with QS implementation.
+
+    private ArrayList<UserData> loadUsers() {
+        ArrayList<UserInfo> users = (ArrayList<UserInfo>) mUserManager
+                .getUsers(true /* excludeDying */);
+        int N = users.size();
+        ArrayList<UserData> result = new ArrayList<>(N);
+        int currentUser = -1;
+        try {
+            currentUser = ActivityManagerNative.getDefault().getCurrentUser().id;
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couln't get current user.", e);
+        }
+        for (int i = 0; i < N; i++) {
+            UserInfo user = users.get(i);
+            if (user.supportsSwitchTo()) {
+                boolean isCurrent = user.id == currentUser;
+                result.add(new UserData(user, mUserManager.getUserIcon(user.id), isCurrent));
+            }
+        }
+        return result;
+    }
+
+    private void switchUser(int userId) {
+        try {
+            WindowManagerGlobal.getWindowManagerService().lockNow(null);
+            ActivityManagerNative.getDefault().switchUser(userId);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Couldn't switch user.", e);
+        }
+    }
+
+    private static class UserData {
+        final UserInfo userInfo;
+        final Bitmap userIcon;
+        final boolean isCurrent;
+
+        UserData(UserInfo userInfo, Bitmap userIcon, boolean isCurrent) {
+            this.userInfo = userInfo;
+            this.userIcon = userIcon;
+            this.isCurrent = isCurrent;
+        }
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index 6d92b05..fcc951e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -32,6 +32,7 @@
     private float mOverScrollTopAmount;
     private float mOverScrollBottomAmount;
     private int mSpeedBumpIndex = -1;
+    private float mScrimAmount;
 
     public int getScrollY() {
         return mScrollY;
@@ -85,6 +86,14 @@
         }
     }
 
+    public void setScrimAmount(float scrimAmount) {
+        mScrimAmount = scrimAmount;
+    }
+
+    public float getScrimAmount() {
+        return mScrimAmount;
+    }
+
     public float getOverScrollAmount(boolean top) {
         return top ? mOverScrollTopAmount : mOverScrollBottomAmount;
     }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 4cbb06b..f6e9aef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -541,8 +541,9 @@
             if (slidingChild.getVisibility() == GONE) {
                 continue;
             }
-            float top = slidingChild.getTranslationY();
-            float bottom = top + slidingChild.getActualHeight();
+            float childTop = slidingChild.getTranslationY();
+            float top = childTop + slidingChild.getClipTopAmount();
+            float bottom = childTop + slidingChild.getActualHeight();
             int left = slidingChild.getLeft();
             int right = slidingChild.getRight();
 
@@ -969,7 +970,7 @@
      * @param animate Should an animation be performed.
      */
     public void setOverScrolledPixels(float numPixels, boolean onTop, boolean animate) {
-        setOverScrollAmount(numPixels * getRubberBandFactor(), onTop, animate, true);
+        setOverScrollAmount(numPixels * getRubberBandFactor(onTop), onTop, animate, true);
     }
 
     /**
@@ -1005,7 +1006,7 @@
         if (animate) {
             mStateAnimator.animateOverScrollToAmount(amount, onTop);
         } else {
-            setOverScrolledPixels(amount / getRubberBandFactor(), onTop);
+            setOverScrolledPixels(amount / getRubberBandFactor(onTop), onTop);
             mAmbientState.setOverScrollAmount(amount, onTop);
             if (onTop) {
                 notifyOverscrollTopListener(amount);
@@ -1227,13 +1228,14 @@
                 mOwnScrollY -= (int) topAmount;
                 mDontReportNextOverScroll = true;
                 setOverScrollAmount(0, true, false);
-                mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor()
+                mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor(true /* onTop */)
                         * mOverflingDistance + topAmount;
             } else if (velocityY > 0 && bottomAmount > 0) {
                 mOwnScrollY += bottomAmount;
                 setOverScrollAmount(0, false, false);
-                mMaxOverScroll = Math.abs(velocityY) / 1000f * getRubberBandFactor()
-                        * mOverflingDistance + bottomAmount;
+                mMaxOverScroll = Math.abs(velocityY) / 1000f
+                        * getRubberBandFactor(false /* onTop */) * mOverflingDistance
+                        +  bottomAmount;
             } else {
                 // it will be set once we reach the boundary
                 mMaxOverScroll = 0.0f;
@@ -1275,7 +1277,10 @@
         return Math.max(desiredPadding, mIntrinsicPadding);
     }
 
-    private float getRubberBandFactor() {
+    private float getRubberBandFactor(boolean onTop) {
+        if (!onTop) {
+            return RUBBER_BAND_FACTOR_NORMAL;
+        }
         if (mExpandedInThisMotion) {
             return RUBBER_BAND_FACTOR_AFTER_EXPAND;
         } else if (mIsExpansionChanging) {
@@ -1841,6 +1846,11 @@
         return true;
     }
 
+    public void setScrimAlpha(float progress) {
+        mAmbientState.setScrimAmount(progress);
+        requestChildrenUpdate();
+    }
+
     /**
      * A listener that is notified when some child locations might have changed.
      */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
index 602c22b..9a4b798 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java
@@ -154,6 +154,17 @@
         handleDraggedViews(ambientState, resultState, algorithmState);
         updateDimmedActivated(ambientState, resultState, algorithmState);
         updateClipping(resultState, algorithmState);
+        updateScrimAmount(resultState, algorithmState, ambientState.getScrimAmount());
+    }
+
+    private void updateScrimAmount(StackScrollState resultState,
+            StackScrollAlgorithmState algorithmState, float scrimAmount) {
+        int childCount = algorithmState.visibleChildren.size();
+        for (int i = 0; i < childCount; i++) {
+            View child = algorithmState.visibleChildren.get(i);
+            StackScrollState.ViewState childViewState = resultState.getViewStateForView(child);
+            childViewState.scrimAmount = scrimAmount;
+        }
     }
 
     private void updateClipping(StackScrollState resultState,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
index 1ad4acc..02f2cd6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollState.java
@@ -148,6 +148,9 @@
                 // apply dimming
                 child.setDimmed(state.dimmed, false /* animate */);
 
+                // apply scrimming
+                child.setScrimAmount(state.scrimAmount);
+
                 float oldClipTopAmount = child.getClipTopAmount();
                 if (oldClipTopAmount != state.clipTopAmount) {
                     child.setClipTopAmount(state.clipTopAmount);
@@ -223,6 +226,12 @@
         boolean dimmed;
 
         /**
+         * A value between 0 and 1 indicating how much the view should be scrimmed.
+         * 1 means that the notifications will be darkened as much as possible.
+         */
+        float scrimAmount;
+
+        /**
          * The amount which the view should be clipped from the top. This is calculated to
          * perceive consistent shadows.
          */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
index 225398a..0006dad 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackStateAnimator.java
@@ -176,6 +176,9 @@
         // start dimmed animation
         child.setDimmed(viewState.dimmed, mAnimationFilter.animateDimmed);
 
+        // apply scrimming
+        child.setScrimAmount(viewState.scrimAmount);
+
         if (wasAdded) {
             child.performAddAnimation(delay);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
index 9260aac..e354166 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tv/TvStatusBar.java
@@ -47,27 +47,15 @@
     }
 
     @Override
-    public void addNotification(StatusBarNotification notification) {
+    public void addNotification(StatusBarNotification notification, RankingMap ranking) {
     }
 
     @Override
-    public void addNotificationInternal(StatusBarNotification notification, RankingMap ranking) {
+    protected void updateNotificationRanking(RankingMap ranking) {
     }
 
     @Override
-    protected void updateRankingInternal(RankingMap ranking) {
-    }
-
-    @Override
-    public void updateNotification(StatusBarNotification notification) {
-    }
-
-    @Override
-    public void removeNotificationInternal(String key, RankingMap ranking) {
-    }
-
-    @Override
-    public void removeNotification(String key) {
+    public void removeNotification(String key, RankingMap ranking) {
     }
 
     @Override
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index bc2671011..ddfa9c1 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -42,7 +42,6 @@
 import android.graphics.PixelFormat;
 import android.graphics.Rect;
 import android.media.AudioManager;
-import android.media.AudioSystem;
 import android.media.IAudioService;
 import android.media.Ringtone;
 import android.media.RingtoneManager;
@@ -51,7 +50,6 @@
 import android.os.FactoryTest;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.IRemoteCallback;
 import android.os.Looper;
 import android.os.Message;
 import android.os.Messenger;
@@ -105,7 +103,6 @@
 import com.android.internal.policy.impl.keyguard.KeyguardServiceDelegate;
 import com.android.internal.policy.impl.keyguard.KeyguardServiceDelegate.ShowListener;
 import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.telephony.ITelephony;
 import com.android.internal.widget.PointerLocationView;
 import com.android.server.LocalServices;
 
@@ -270,10 +267,10 @@
     // The following are only accessed on the mHandler thread.
     boolean mKeyguardDrawComplete;
     boolean mWindowManagerDrawComplete;
-    ArrayList<ScreenOnListener> mScreenOnListeners = new ArrayList<ScreenOnListener>();
-    final IRemoteCallback mWindowManagerDrawCallback = new IRemoteCallback.Stub() {
+    ScreenOnListener mScreenOnListener;
+    final Runnable mWindowManagerDrawCallback = new Runnable() {
         @Override
-        public void sendResult(Bundle data) {
+        public void run() {
             if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display!");
             mHandler.sendEmptyMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE);
         }
@@ -521,6 +518,7 @@
     private static final int MSG_KEYGUARD_DRAWN_TIMEOUT = 6;
     private static final int MSG_WINDOW_MANAGER_DRAWN_COMPLETE = 7;
     private static final int MSG_WAKING_UP = 8;
+    private static final int MSG_DISPATCH_SHOW_RECENTS = 9;
 
     private class PolicyHandler extends Handler {
         @Override
@@ -538,6 +536,9 @@
                 case MSG_DISPATCH_MEDIA_KEY_REPEAT_WITH_WAKE_LOCK:
                     dispatchMediaKeyRepeatWithWakeLock((KeyEvent)msg.obj);
                     break;
+                case MSG_DISPATCH_SHOW_RECENTS:
+                    showRecentApps(false);
+                    break;
                 case MSG_KEYGUARD_DRAWN_COMPLETE:
                     if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mKeyguardDrawComplete");
                     mKeyguardDrawComplete = true;
@@ -624,6 +625,7 @@
         public void onWakeUp() {
             synchronized (mLock) {
                 if (shouldEnableWakeGestureLp()) {
+                    performHapticFeedbackLw(null, HapticFeedbackConstants.VIRTUAL_KEY, false);
                     mPowerManager.wakeUp(SystemClock.uptimeMillis());
                 }
             }
@@ -2588,6 +2590,12 @@
         }
     }
 
+    @Override
+    public void showRecentApps() {
+        mHandler.removeMessages(MSG_DISPATCH_SHOW_RECENTS);
+        mHandler.sendEmptyMessage(MSG_DISPATCH_SHOW_RECENTS);
+    }
+
     private void showRecentApps(boolean triggeredFromAltTab) {
         mPreloadedRecentApps = false; // preloading no longer needs to be canceled
         try {
@@ -2638,19 +2646,22 @@
                     }
                 }
             });
-        } else if (mRecentsVisible) {
-            // Recents is started on top of Home, so when we launch home while recents is open, let
-            // it do its own animation and then finish itself
-            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
-            hideRecentApps(false);
         } else {
             // no keyguard stuff to worry about, just launch home!
             try {
                 ActivityManagerNative.getDefault().stopAppSwitches();
             } catch (RemoteException e) {
             }
-            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
-            startDockOrHome();
+            if (mRecentsVisible) {
+                // Hide Recents and notify it to launch Home
+                awakenDreams();
+                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+                hideRecentApps(false);
+            } else {
+                // Otherwise, just launch Home
+                sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);
+                startDockOrHome();
+            }
         }
     }
 
@@ -4098,6 +4109,9 @@
         // If the key would be handled globally, just return the result, don't worry about special
         // key processing.
         if (mGlobalKeyManager.shouldHandleGlobalKey(keyCode, event)) {
+            if (isWakeKey) {
+                mPowerManager.wakeUp(event.getEventTime());
+            }
             return result;
         }
 
@@ -4542,7 +4556,7 @@
     // Called on the mHandler thread.
     private void handleWakingUp(final ScreenOnListener screenOnListener) {
         if (screenOnListener != null) {
-            mScreenOnListeners.add(screenOnListener);
+            mScreenOnListener = screenOnListener;
         }
 
         synchronized (mLock) {
@@ -4574,8 +4588,11 @@
             return;
         }
 
+        ScreenOnListener screenOnListener;
         synchronized (mLock) {
             mScreenOnFully = true;
+            screenOnListener = mScreenOnListener;
+            mScreenOnListener = null;
         }
 
         try {
@@ -4583,8 +4600,8 @@
         } catch (RemoteException unhandled) {
         }
 
-        for (int i = mScreenOnListeners.size() - 1; i >=0; --i) {
-            mScreenOnListeners.remove(i).onScreenOn();
+        if (screenOnListener != null) {
+            screenOnListener.onScreenOn();
         }
 
         setKeyguardDrawn();
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index 2191b54..d9588e8 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -770,10 +770,11 @@
         mRS.validate();
         int eSize = mType.mElement.getBytesSize();
         final byte[] data = fp.getData();
+        int data_length = fp.getPos();
 
-        int count = data.length / eSize;
-        if ((eSize * count) != data.length) {
-            throw new RSIllegalArgumentException("Field packer length " + data.length +
+        int count = data_length / eSize;
+        if ((eSize * count) != data_length) {
+            throw new RSIllegalArgumentException("Field packer length " + data_length +
                                                " not divisible by element size " + eSize + ".");
         }
         copy1DRangeFromUnchecked(xoff, count, data);
@@ -797,16 +798,17 @@
         }
 
         final byte[] data = fp.getData();
+        int data_length = fp.getPos();
         int eSize = mType.mElement.mElements[component_number].getBytesSize();
         eSize *= mType.mElement.mArraySizes[component_number];
 
-        if (data.length != eSize) {
-            throw new RSIllegalArgumentException("Field packer sizelength " + data.length +
+        if (data_length != eSize) {
+            throw new RSIllegalArgumentException("Field packer sizelength " + data_length +
                                                " does not match component size " + eSize + ".");
         }
 
         mRS.nAllocationElementData1D(getIDSafe(), xoff, mSelectedLOD,
-                                     component_number, data, data.length);
+                                     component_number, data, data_length);
     }
 
     private void data1DChecks(int off, int count, int len, int dataSize) {
diff --git a/rs/java/android/renderscript/Element.java b/rs/java/android/renderscript/Element.java
index 55b671d..c6b5b0d 100644
--- a/rs/java/android/renderscript/Element.java
+++ b/rs/java/android/renderscript/Element.java
@@ -140,17 +140,17 @@
         MATRIX_3X3 (17, 36),
         MATRIX_2X2 (18, 16),
 
-        RS_ELEMENT (1000, 4),
-        RS_TYPE (1001, 4),
-        RS_ALLOCATION (1002, 4),
-        RS_SAMPLER (1003, 4),
-        RS_SCRIPT (1004, 4),
-        RS_MESH (1005, 4),
-        RS_PROGRAM_FRAGMENT (1006, 4),
-        RS_PROGRAM_VERTEX (1007, 4),
-        RS_PROGRAM_RASTER (1008, 4),
-        RS_PROGRAM_STORE (1009, 4),
-        RS_FONT (1010, 4);
+        RS_ELEMENT (1000),
+        RS_TYPE (1001),
+        RS_ALLOCATION (1002),
+        RS_SAMPLER (1003),
+        RS_SCRIPT (1004),
+        RS_MESH (1005),
+        RS_PROGRAM_FRAGMENT (1006),
+        RS_PROGRAM_VERTEX (1007),
+        RS_PROGRAM_RASTER (1008),
+        RS_PROGRAM_STORE (1009),
+        RS_FONT (1010);
 
         int mID;
         int mSize;
@@ -158,6 +158,14 @@
             mID = id;
             mSize = size;
         }
+
+        DataType(int id) {
+            mID = id;
+            mSize = 4;
+            if (RenderScript.sPointerSize == 8) {
+                mSize = 32;
+            }
+        }
     }
 
     /**
diff --git a/rs/java/android/renderscript/FieldPacker.java b/rs/java/android/renderscript/FieldPacker.java
index c9bba69..20b07e7 100644
--- a/rs/java/android/renderscript/FieldPacker.java
+++ b/rs/java/android/renderscript/FieldPacker.java
@@ -36,10 +36,15 @@
     }
 
     public FieldPacker(byte[] data) {
-        mPos = 0;
+        // Advance mPos to the end of the buffer, since we are copying in the
+        // full data input.
+        mPos = data.length;
         mLen = data.length;
         mData = data;
         mAlignment = new BitSet();
+        // TODO: We should either have an actual FieldPacker copy constructor
+        // or drop support for computing alignment like this. As it stands,
+        // subAlign() can never work correctly for copied FieldPacker objects.
     }
 
     public void align(int v) {
@@ -75,7 +80,7 @@
         mPos = 0;
     }
     public void reset(int i) {
-        if ((i < 0) || (i >= mLen)) {
+        if ((i < 0) || (i > mLen)) {
             throw new RSIllegalArgumentException("out of range argument: " + i);
         }
         mPos = i;
@@ -605,6 +610,15 @@
         return mData;
     }
 
+    /**
+     * Get the actual length used for the FieldPacker.
+     *
+     * @hide
+     */
+    public int getPos() {
+        return mPos;
+    }
+
     private final byte mData[];
     private int mPos;
     private int mLen;
diff --git a/rs/java/android/renderscript/Font.java b/rs/java/android/renderscript/Font.java
index b22aeb7..4318b9d 100644
--- a/rs/java/android/renderscript/Font.java
+++ b/rs/java/android/renderscript/Font.java
@@ -111,10 +111,10 @@
 
         FontFamily serifFamily = new FontFamily();
         serifFamily.mNames = sSerifNames;
-        serifFamily.mNormalFileName = "DroidSerif-Regular.ttf";
-        serifFamily.mBoldFileName = "DroidSerif-Bold.ttf";
-        serifFamily.mItalicFileName = "DroidSerif-Italic.ttf";
-        serifFamily.mBoldItalicFileName = "DroidSerif-BoldItalic.ttf";
+        serifFamily.mNormalFileName = "NotoSerif-Regular.ttf";
+        serifFamily.mBoldFileName = "NotoSerif-Bold.ttf";
+        serifFamily.mItalicFileName = "NotoSerif-Italic.ttf";
+        serifFamily.mBoldItalicFileName = "NotoSerif-BoldItalic.ttf";
         addFamilyToMap(serifFamily);
 
         FontFamily monoFamily = new FontFamily();
diff --git a/rs/java/android/renderscript/ProgramVertexFixedFunction.java b/rs/java/android/renderscript/ProgramVertexFixedFunction.java
index 5173af2..45840ae 100644
--- a/rs/java/android/renderscript/ProgramVertexFixedFunction.java
+++ b/rs/java/android/renderscript/ProgramVertexFixedFunction.java
@@ -245,6 +245,9 @@
             for(int i = 0; i < 16; i ++) {
                 mIOBuffer.addF32(m.mMat[i]);
             }
+            // Reset the buffer back to the end, since we want to flush all of
+            // the contents back (and not just what we wrote now).
+            mIOBuffer.reset(mIOBuffer.getData().length);
             mAlloc.setFromFieldPacker(0, mIOBuffer);
         }
 
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 9e893da..9f080ca 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -219,6 +219,7 @@
         // Wait for a down key event to start processing.
         if (!mKeyEventSequenceStarted) {
             if (event.getAction() != KeyEvent.ACTION_DOWN) {
+                super.onInputEvent(event, policyFlags);
                 return;
             }
             mKeyEventSequenceStarted = true;
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index cef6830..3b1e88a 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -28,6 +28,7 @@
 import android.app.backup.BackupDataOutput;
 import android.app.backup.BackupTransport;
 import android.app.backup.FullBackup;
+import android.app.backup.RestoreDescription;
 import android.app.backup.RestoreSet;
 import android.app.backup.IBackupManager;
 import android.app.backup.IFullBackupRestoreObserver;
@@ -5177,7 +5178,9 @@
 
         void restorePmMetadata() {
             try {
-                String packageName = mTransport.nextRestorePackage();
+                RestoreDescription desc = mTransport.nextRestorePackage();
+                // TODO: handle full-data stream restore payloads
+                String packageName = desc.getPackageName();
                 if (packageName == null) {
                     Slog.e(TAG, "Error getting first restore package");
                     EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
@@ -5245,7 +5248,9 @@
 
         void restoreNextAgent() {
             try {
-                String packageName = mTransport.nextRestorePackage();
+                final RestoreDescription desc = mTransport.nextRestorePackage();
+                // TODO: handle full-data stream restore payloads
+                String packageName = desc.getPackageName();
 
                 if (packageName == null) {
                     Slog.e(TAG, "Error getting next restore package");
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index aeb195f..912a181 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -18,6 +18,7 @@
 
 import android.database.ContentObserver;
 import android.os.BatteryStats;
+
 import com.android.internal.app.IBatteryStats;
 import com.android.server.am.BatteryStatsService;
 import com.android.server.lights.Light;
@@ -29,6 +30,7 @@
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
 import android.os.BatteryProperties;
 import android.os.Binder;
 import android.os.FileUtils;
@@ -83,7 +85,7 @@
  * service asynchronously itself.
  * </p>
  */
-public final class BatteryService extends Binder {
+public final class BatteryService extends SystemService {
     private static final String TAG = BatteryService.class.getSimpleName();
 
     private static final boolean DEBUG = false;
@@ -140,10 +142,12 @@
 
     private boolean mSentLowBatteryBroadcast = false;
 
-    public BatteryService(Context context, LightsManager lightsManager) {
+    public BatteryService(Context context) {
+        super(context);
+
         mContext = context;
         mHandler = new Handler(true /*async*/);
-        mLed = new Led(context, lightsManager);
+        mLed = new Led(context, getLocalService(LightsManager.class));
         mBatteryStats = BatteryStatsService.getService();
 
         mCriticalBatteryLevel = mContext.getResources().getInteger(
@@ -160,7 +164,10 @@
             mInvalidChargerObserver.startObserving(
                     "DEVPATH=/devices/virtual/switch/invalid_charger");
         }
+    }
 
+    @Override
+    public void onStart() {
         IBinder b = ServiceManager.getService("batteryproperties");
         final IBatteryPropertiesRegistrar batteryPropertiesRegistrar =
                 IBatteryPropertiesRegistrar.Stub.asInterface(b);
@@ -169,28 +176,34 @@
         } catch (RemoteException e) {
             // Should never happen.
         }
+
+        publishBinderService("battery", new BinderService());
+        publishLocalService(BatteryManagerInternal.class, new LocalService());
     }
 
-    void systemReady() {
-        // check our power situation now that it is safe to display the shutdown dialog.
-        synchronized (mLock) {
-            ContentObserver obs = new ContentObserver(mHandler) {
-                @Override
-                public void onChange(boolean selfChange) {
-                    synchronized (mLock) {
-                        updateBatteryWarningLevelLocked();
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+            // check our power situation now that it is safe to display the shutdown dialog.
+            synchronized (mLock) {
+                ContentObserver obs = new ContentObserver(mHandler) {
+                    @Override
+                    public void onChange(boolean selfChange) {
+                        synchronized (mLock) {
+                            updateBatteryWarningLevelLocked();
+                        }
                     }
-                }
-            };
-            final ContentResolver resolver = mContext.getContentResolver();
-            resolver.registerContentObserver(Settings.Global.getUriFor(
-                    Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
-                    false, obs, UserHandle.USER_ALL);
-            updateBatteryWarningLevelLocked();
+                };
+                final ContentResolver resolver = mContext.getContentResolver();
+                resolver.registerContentObserver(Settings.Global.getUriFor(
+                        Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL),
+                        false, obs, UserHandle.USER_ALL);
+                updateBatteryWarningLevelLocked();
+            }
         }
     }
 
-    void updateBatteryWarningLevelLocked() {
+    private void updateBatteryWarningLevelLocked() {
         final ContentResolver resolver = mContext.getContentResolver();
         int defWarnLevel = mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_lowBatteryWarningLevel);
@@ -207,15 +220,6 @@
         processValuesLocked(true);
     }
 
-    /**
-     * Returns true if the device is plugged into any of the specified plug types.
-     */
-    public boolean isPowered(int plugTypeSet) {
-        synchronized (mLock) {
-            return isPoweredLocked(plugTypeSet);
-        }
-    }
-
     private boolean isPoweredLocked(int plugTypeSet) {
         // assume we are powered if battery state is unknown so
         // the "stay on while plugged in" option will work.
@@ -234,34 +238,7 @@
         return false;
     }
 
-    /**
-     * Returns the current plug type.
-     */
-    public int getPlugType() {
-        synchronized (mLock) {
-            return mPlugType;
-        }
-    }
-
-    /**
-     * Returns battery level as a percentage.
-     */
-    public int getBatteryLevel() {
-        synchronized (mLock) {
-            return mBatteryProps.batteryLevel;
-        }
-    }
-
-    /**
-     * Returns whether we currently consider the battery level to be low.
-     */
-    public boolean getBatteryLevelLow() {
-        synchronized (mLock) {
-            return mBatteryLevelLow;
-        }
-    }
-
-    public boolean shouldSendBatteryLowLocked() {
+    private boolean shouldSendBatteryLowLocked() {
         final boolean plugged = mPlugType != BATTERY_PLUGGED_NONE;
         final boolean oldPlugged = mLastPlugType != BATTERY_PLUGGED_NONE;
 
@@ -277,15 +254,6 @@
                 && (oldPlugged || mLastBatteryLevel > mLowBatteryWarningLevel);
     }
 
-    /**
-     * Returns a non-zero value if an  unsupported charger is attached.
-     */
-    public int getInvalidCharger() {
-        synchronized (mLock) {
-            return mInvalidCharger;
-        }
-    }
-
     private void shutdownIfNoPowerLocked() {
         // shut down gracefully if our battery is critically low and we are not powered.
         // wait until the system has booted before attempting to display the shutdown dialog.
@@ -640,17 +608,7 @@
         }
     }
 
-    @Override
-    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
-                != PackageManager.PERMISSION_GRANTED) {
-
-            pw.println("Permission Denial: can't dump Battery service from from pid="
-                    + Binder.getCallingPid()
-                    + ", uid=" + Binder.getCallingUid());
-            return;
-        }
-
+    private void dumpInternal(PrintWriter pw, String[] args) {
         synchronized (mLock) {
             if (args == null || args.length == 0 || "-a".equals(args[0])) {
                 pw.println("Current Battery Service state:");
@@ -801,4 +759,57 @@
             }
        }
     }
+
+    private final class BinderService extends Binder {
+        @Override
+        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+            if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+                    != PackageManager.PERMISSION_GRANTED) {
+
+                pw.println("Permission Denial: can't dump Battery service from from pid="
+                        + Binder.getCallingPid()
+                        + ", uid=" + Binder.getCallingUid());
+                return;
+            }
+
+            dumpInternal(pw, args);
+        }
+    }
+
+    private final class LocalService extends BatteryManagerInternal {
+        @Override
+        public boolean isPowered(int plugTypeSet) {
+            synchronized (mLock) {
+                return isPoweredLocked(plugTypeSet);
+            }
+        }
+
+        @Override
+        public int getPlugType() {
+            synchronized (mLock) {
+                return mPlugType;
+            }
+        }
+
+        @Override
+        public int getBatteryLevel() {
+            synchronized (mLock) {
+                return mBatteryProps.batteryLevel;
+            }
+        }
+
+        @Override
+        public boolean getBatteryLevelLow() {
+            synchronized (mLock) {
+                return mBatteryLevelLow;
+            }
+        }
+
+        @Override
+        public int getInvalidCharger() {
+            synchronized (mLock) {
+                return mInvalidCharger;
+            }
+        }
+    }
 }
diff --git a/services/core/java/com/android/server/IdleMaintenanceService.java b/services/core/java/com/android/server/IdleMaintenanceService.java
deleted file mode 100644
index acc6abe..0000000
--- a/services/core/java/com/android/server/IdleMaintenanceService.java
+++ /dev/null
@@ -1,818 +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;
-
-import android.app.AlarmManager;
-import android.app.PendingIntent;
-import android.app.maintenance.IIdleCallback;
-import android.app.maintenance.IIdleService;
-import android.app.maintenance.IdleService;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-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.os.Handler;
-import android.os.PowerManager;
-import android.os.PowerManager.WakeLock;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.SystemClock;
-import android.os.UserHandle;
-import android.os.WorkSource;
-import android.util.ArrayMap;
-import android.util.Log;
-import android.util.Slog;
-import android.util.SparseArray;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Random;
-
-/**
- * This service observes the device state and when applicable sends
- * broadcasts at the beginning and at the end of a period during which
- * observers can perform idle maintenance tasks. Typical use of the
- * idle maintenance is to perform somehow expensive tasks that can be
- * postponed to a moment when they will not degrade user experience.
- *
- * The current implementation is very simple. The start of a maintenance
- * window is announced if: the screen is off or showing a dream AND the
- * battery level is more than twenty percent AND at least one hour passed
- * activity).
- *
- * The end of a maintenance window is announced only if: a start was
- * announced AND the screen turned on or a dream was stopped.
- *
- * Method naming note:
- * Methods whose name ends with "Tm" must only be called from the main thread.
- */
-public class IdleMaintenanceService extends BroadcastReceiver {
-
-    private static final boolean DEBUG = false;
-
-    private static final String TAG = IdleMaintenanceService.class.getSimpleName();
-
-    private static final int LAST_USER_ACTIVITY_TIME_INVALID = -1;
-
-    private static final long MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 1 day
-
-    private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING = 30; // percent
-
-    private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING = 80; // percent
-
-    private static final int MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING = 20; // percent
-
-    private static final long MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START = 71 * 60 * 1000; // 71 min
-
-    private static final long MAX_IDLE_MAINTENANCE_DURATION = 71 * 60 * 1000; // 71 min
-
-    private static final String ACTION_UPDATE_IDLE_MAINTENANCE_STATE =
-        "com.android.server.IdleMaintenanceService.action.UPDATE_IDLE_MAINTENANCE_STATE";
-
-    private static final String ACTION_FORCE_IDLE_MAINTENANCE =
-        "com.android.server.IdleMaintenanceService.action.FORCE_IDLE_MAINTENANCE";
-
-    static final int MSG_OP_COMPLETE = 1;
-    static final int MSG_IDLE_FINISHED = 2;
-    static final int MSG_TIMEOUT = 3;
-
-    // when a timeout happened, what were we expecting?
-    static final int VERB_BINDING = 1;
-    static final int VERB_IDLING = 2;
-    static final int VERB_ENDING = 3;
-
-    // What are our relevant timeouts / allocated slices?
-    static final long OP_TIMEOUT = 8 * 1000;  // 8 seconds to bind or ack the start
-    static final long IDLE_TIMESLICE = 10 * 60 * 1000;  // ten minutes for each idler
-
-    private final AlarmManager mAlarmService;
-    private final BatteryService mBatteryService;
-    private final PendingIntent mUpdateIdleMaintenanceStatePendingIntent;
-    private final Context mContext;
-    private final WakeLock mWakeLock;
-    private final WorkSource mSystemWorkSource = new WorkSource(Process.myUid());
-
-    private long mLastIdleMaintenanceStartTimeMillis;
-    private long mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
-    private boolean mIdleMaintenanceStarted;
-
-    final IdleCallback mCallback;
-    final Handler mHandler;
-
-    final Random mTokenGenerator = new Random();
-
-    int makeToken() {
-        int token;
-        do  {
-            token = mTokenGenerator.nextInt(Integer.MAX_VALUE);
-        } while (token == 0);
-        return token;
-    }
-
-    class ActiveTask {
-        public IdleServiceInfo who;
-        public int verb;
-        public int token;
-
-        ActiveTask(IdleServiceInfo target, int action) {
-            who = target;
-            verb = action;
-            token = makeToken();
-        }
-
-        @Override
-        public String toString() {
-            return "ActiveTask{" + Integer.toHexString(this.hashCode())
-                    + " : verb=" + verb
-                    + " : token=" + token
-                    + " : "+ who + "}";
-        }
-    }
-
-    // What operations are in flight?
-    final SparseArray<ActiveTask> mPendingOperations = new SparseArray<ActiveTask>();
-
-    // Idle service queue management
-    class IdleServiceInfo {
-        public final ComponentName componentName;
-        public final int uid;
-        public IIdleService service;
-
-        IdleServiceInfo(ResolveInfo info, ComponentName cname) {
-            componentName = cname;  // derived from 'info' but this avoids an extra object
-            uid = info.serviceInfo.applicationInfo.uid;
-            service = null;
-        }
-
-        @Override
-        public int hashCode() {
-            return componentName.hashCode();
-        }
-
-        @Override
-        public String toString() {
-            return "IdleServiceInfo{" + componentName
-                    + " / " + (service == null ? "null" : service.asBinder()) + "}";
-        }
-    }
-
-    final ArrayMap<ComponentName, IdleServiceInfo> mIdleServices =
-            new ArrayMap<ComponentName, IdleServiceInfo>();
-    final LinkedList<IdleServiceInfo> mIdleServiceQueue = new LinkedList<IdleServiceInfo>();
-    IdleServiceInfo mCurrentIdler;  // set when we've committed to launching an idler
-    IdleServiceInfo mLastIdler;     // end of queue when idling begins
-
-    void reportNoTimeout(int token, boolean result) {
-        final Message msg = mHandler.obtainMessage(MSG_OP_COMPLETE, result ? 1 : 0, token);
-        mHandler.sendMessage(msg);
-    }
-
-    // Binder acknowledgment trampoline
-    class IdleCallback extends IIdleCallback.Stub {
-        @Override
-        public void acknowledgeStart(int token, boolean result) throws RemoteException {
-            reportNoTimeout(token, result);
-        }
-
-        @Override
-        public void acknowledgeStop(int token) throws RemoteException {
-            reportNoTimeout(token, false);
-        }
-
-        @Override
-        public void idleFinished(int token) throws RemoteException {
-            if (DEBUG) {
-                Slog.v(TAG, "idleFinished: " + token);
-            }
-            final Message msg = mHandler.obtainMessage(MSG_IDLE_FINISHED, 0, token);
-            mHandler.sendMessage(msg);
-        }
-    }
-
-    // Stuff that we run on a Handler
-    class IdleHandler extends Handler {
-        public IdleHandler(Looper looper) {
-            super(looper);
-        }
-
-        @Override
-        public void handleMessage(Message msg) {
-            final int token = msg.arg2;
-
-            switch (msg.what) {
-                case MSG_OP_COMPLETE: {
-                    if (DEBUG) {
-                        Slog.i(TAG, "MSG_OP_COMPLETE of " + token);
-                    }
-                    ActiveTask task = mPendingOperations.get(token);
-                    if (task != null) {
-                        mPendingOperations.remove(token);
-                        removeMessages(MSG_TIMEOUT);
-
-                        handleOpCompleteTm(task, msg.arg1);
-                    } else {
-                        // Can happen in a race between timeout and actual
-                        // (belated) completion of a "begin idling" or similar
-                        // operation.  In that state we've already processed the
-                        // timeout, so we intentionally no-op here.
-                        if (DEBUG) {
-                            Slog.w(TAG, "Belated op-complete of " + token);
-                        }
-                    }
-                    break;
-                }
-
-                case MSG_IDLE_FINISHED: {
-                    if (DEBUG) {
-                        Slog.i(TAG, "MSG_IDLE_FINISHED of " + token);
-                    }
-                    ActiveTask task = mPendingOperations.get(token);
-                    if (task != null) {
-                        if (DEBUG) {
-                            Slog.i(TAG, "... removing task " + token);
-                        }
-                        mPendingOperations.remove(token);
-                        removeMessages(MSG_TIMEOUT);
-
-                        handleIdleFinishedTm(task);
-                    } else {
-                        // Can happen "legitimately" from an app explicitly calling
-                        // idleFinished() after already having been told that its slice
-                        // has ended.
-                        if (DEBUG) {
-                            Slog.w(TAG, "Belated idle-finished of " + token);
-                        }
-                    }
-                    break;
-                }
-
-                case MSG_TIMEOUT: {
-                    if (DEBUG) {
-                        Slog.i(TAG, "MSG_TIMEOUT of " + token);
-                    }
-                    ActiveTask task = mPendingOperations.get(token);
-                    if (task != null) {
-                        mPendingOperations.remove(token);
-                        removeMessages(MSG_OP_COMPLETE);
-
-                        handleTimeoutTm(task);
-                    } else {
-                        // This one should not happen; we flushed timeout messages
-                        // whenever we entered a state after which we have established
-                        // that they are not appropriate.
-                        Slog.w(TAG, "Unexpected timeout of " + token);
-                    }
-                    break;
-                }
-
-                default:
-                    Slog.w(TAG, "Unknown message: " + msg.what);
-            }
-        }
-    }
-
-    void handleTimeoutTm(ActiveTask task) {
-        switch (task.verb) {
-        case VERB_BINDING: {
-            // We were trying to bind to this service, but it wedged or otherwise
-            // failed to respond in time.  Let it stay in the queue for the next
-            // time around, but just give up on it for now and go on to the next.
-            startNextIdleServiceTm();
-            break;
-        }
-        case VERB_IDLING: {
-            // The service has reached the end of its designated idle timeslice.
-            // This is not considered an error.
-            if (DEBUG) {
-                Slog.i(TAG, "Idler reached end of timeslice: " + task.who);
-            }
-            sendEndIdleTm(task.who);
-            break;
-        }
-        case VERB_ENDING: {
-            if (mCurrentIdler == task.who) {
-                if (DEBUG) {
-                    Slog.i(TAG, "Task timed out when ending; unbind needed");
-                }
-                handleIdleFinishedTm(task);
-            } else {
-                if (DEBUG) {
-                    Slog.w(TAG, "Ending timeout for non-current idle service!");
-                }
-            }
-            break;
-        }
-        default: {
-            Slog.w(TAG, "Unknown timeout state " + task.verb);
-            break;
-        }
-        }
-    }
-
-    void handleOpCompleteTm(ActiveTask task, int result) {
-        if (DEBUG) {
-            Slog.i(TAG, "handleOpComplete : task=" + task + " result=" + result);
-        }
-        if (task.verb == VERB_IDLING) {
-            // If the service was told to begin idling and responded positively, then
-            // it has begun idling and will eventually either explicitly finish, or
-            // reach the end of its allotted timeslice.  It's running free now, so we
-            // just schedule the idle-expiration timeout under the token it's already been
-            // given and let it keep going.
-            if (result != 0) {
-                scheduleOpTimeoutTm(task);
-            } else {
-                // The idle service has indicated that it does not, in fact,
-                // need to run at present, so we immediately indicate that it's
-                // to finish idling, and go on to the next idler.
-                if (DEBUG) {
-                    Slog.i(TAG, "Idler declined idling; moving along");
-                }
-                sendEndIdleTm(task.who);
-            }
-        } else {
-            // In the idling case, the task will be cleared either as the result of a timeout
-            // or of an explicit idleFinished().  For all other operations (binding, ending) we
-            // are done with the task as such, so we remove it from our bookkeeping.
-            if (DEBUG) {
-                Slog.i(TAG, "Clearing task " + task);
-            }
-            mPendingOperations.remove(task.token);
-            if (task.verb == VERB_ENDING) {
-                // The last bit of handshaking around idle cessation for this target
-                handleIdleFinishedTm(task);
-            }
-        }
-    }
-
-    void handleIdleFinishedTm(ActiveTask task) {
-        final IdleServiceInfo who = task.who;
-        if (who == mCurrentIdler) {
-            if (DEBUG) {
-                Slog.i(TAG, "Current idler has finished: " + who);
-                Slog.i(TAG, "Attributing wakelock to system work source");
-            }
-            mContext.unbindService(mConnection);
-            startNextIdleServiceTm();
-        } else {
-            Slog.w(TAG, "finish from non-current idle service? " + who);
-        }
-    }
-
-    void updateIdleServiceQueueTm() {
-        if (DEBUG) {
-            Slog.i(TAG, "Updating idle service queue");
-        }
-        PackageManager pm = mContext.getPackageManager();
-        Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
-        List<ResolveInfo> services = pm.queryIntentServices(idleIntent, 0);
-        for (ResolveInfo info : services) {
-            if (info.serviceInfo != null) {
-                if (IdleService.PERMISSION_BIND.equals(info.serviceInfo.permission)) {
-                    final ComponentName componentName = new ComponentName(
-                            info.serviceInfo.packageName,
-                            info.serviceInfo.name);
-                    if (DEBUG) {
-                        Slog.i(TAG, "   - " + componentName);
-                    }
-                    if (!mIdleServices.containsKey(componentName)) {
-                        if (DEBUG) {
-                            Slog.i(TAG, "      + not known; adding");
-                        }
-                        IdleServiceInfo serviceInfo = new IdleServiceInfo(info, componentName);
-                        mIdleServices.put(componentName, serviceInfo);
-                        mIdleServiceQueue.add(serviceInfo);
-                    }
-                } else {
-                    if (DEBUG) {
-                        Slog.i(TAG, "Idle service " + info.serviceInfo
-                                + " does not have required permission; ignoring");
-                    }
-                }
-            }
-        }
-    }
-
-    void startNextIdleServiceTm() {
-        mWakeLock.setWorkSource(mSystemWorkSource);
-
-        if (mLastIdler == null) {
-            // we've run the queue; nothing more to do until the next idle interval.
-            if (DEBUG) {
-                Slog.i(TAG, "Queue already drained; nothing more to do");
-            }
-            return;
-        }
-
-        if (DEBUG) {
-            Slog.i(TAG, "startNextIdleService : last=" + mLastIdler + " cur=" + mCurrentIdler);
-            if (mIdleServiceQueue.size() > 0) {
-                int i = 0;
-                Slog.i(TAG, "Queue (" + mIdleServiceQueue.size() + "):");
-                for (IdleServiceInfo info : mIdleServiceQueue) {
-                    Slog.i(TAG, "   " + i + " : " + info);
-                    i++;
-                }
-            }
-        }
-        if (mCurrentIdler != mLastIdler) {
-            if (mIdleServiceQueue.size() > 0) {
-                IdleServiceInfo target = mIdleServiceQueue.pop();
-                if (DEBUG) {
-                    Slog.i(TAG, "starting next idle service " + target);
-                }
-                Intent idleIntent = new Intent(IdleService.SERVICE_INTERFACE);
-                idleIntent.setComponent(target.componentName);
-                mCurrentIdler = target;
-                ActiveTask task = new ActiveTask(target, VERB_BINDING);
-                scheduleOpTimeoutTm(task);
-                boolean bindOk = mContext.bindServiceAsUser(idleIntent, mConnection,
-                        Context.BIND_AUTO_CREATE | Context.BIND_WAIVE_PRIORITY, UserHandle.OWNER);
-                if (!bindOk) {
-                    if (DEBUG) {
-                        Slog.w(TAG, "bindService() to " + target.componentName
-                                + " failed");
-                    }
-                } else {
-                    mIdleServiceQueue.add(target);  // at the end for next time
-                    if (DEBUG) { Slog.i(TAG, "Attributing wakelock to target uid " + target.uid); }
-                    mWakeLock.setWorkSource(new WorkSource(target.uid));
-                }
-            } else {
-                // Queue is empty but mLastIdler is non-null -- eeep.  Clear *everything*
-                // and wind up until the next time around.
-                Slog.e(TAG, "Queue unexpectedly empty; resetting.  last="
-                        + mLastIdler + " cur=" + mCurrentIdler);
-                mHandler.removeMessages(MSG_TIMEOUT);
-                mPendingOperations.clear();
-                stopIdleMaintenanceTm();
-            }
-        } else {
-            // we've reached the place we started, so mark the queue as drained
-            if (DEBUG) {
-                Slog.i(TAG, "Reached end of queue.");
-            }
-            stopIdleMaintenanceTm();
-        }
-    }
-
-    void sendStartIdleTm(IdleServiceInfo who) {
-        ActiveTask task = new ActiveTask(who, VERB_IDLING);
-        scheduleOpTimeoutTm(task);
-        try {
-            who.service.startIdleMaintenance(mCallback, task.token);
-        } catch (RemoteException e) {
-            // We bound to it, but now we can't reach it.  Bail and go on to the
-            // next service.
-            mContext.unbindService(mConnection);
-            if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
-            mHandler.removeMessages(MSG_TIMEOUT);
-            startNextIdleServiceTm();
-        }
-    }
-
-    void sendEndIdleTm(IdleServiceInfo who) {
-        ActiveTask task = new ActiveTask(who, VERB_ENDING);
-        scheduleOpTimeoutTm(task);
-        if (DEBUG) {
-            Slog.i(TAG, "Sending end-idle to " + who);
-        }
-        try {
-            who.service.stopIdleMaintenance(mCallback, task.token);
-        } catch (RemoteException e) {
-            // We bound to it, but now we can't reach it.  Bail and go on to the
-            // next service.
-            mContext.unbindService(mConnection);
-            if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
-            mHandler.removeMessages(MSG_TIMEOUT);
-            startNextIdleServiceTm();
-        }
-    }
-
-    ServiceConnection mConnection = new ServiceConnection() {
-        @Override
-        public void onServiceConnected(ComponentName name, IBinder service) {
-            if (DEBUG) {
-                Slog.i(TAG, "onServiceConnected(" + name + ")");
-            }
-            IdleServiceInfo info = mIdleServices.get(name);
-            if (info != null) {
-                // Bound!  Cancel the bind timeout
-                mHandler.removeMessages(MSG_TIMEOUT);
-                // Now tell it to start its idle work
-                info.service = IIdleService.Stub.asInterface(service);
-                sendStartIdleTm(info);
-            } else {
-                // We bound to a service we don't know about.  That's ungood.
-                Slog.e(TAG, "Connected to unexpected component " + name);
-                mContext.unbindService(this);
-            }
-        }
-
-        @Override
-        public void onServiceDisconnected(ComponentName name) {
-            if (DEBUG) {
-                Slog.i(TAG, "onServiceDisconnected(" + name + ")");
-            }
-            IdleServiceInfo who = mIdleServices.get(name);
-            if (who == mCurrentIdler) {
-                // Hm, okay; they didn't tell us they were finished but they
-                // went away.  Crashed, probably.  Oh well.  They're gone, so
-                // we can't finish them cleanly; just force things along.
-                Slog.w(TAG, "Idler unexpectedly vanished: " + mCurrentIdler);
-                mContext.unbindService(this);
-                mHandler.removeMessages(MSG_TIMEOUT);
-                startNextIdleServiceTm();
-            } else {
-                // Not the current idler, so we don't interrupt our process...
-                if (DEBUG) {
-                    Slog.w(TAG, "Disconnect of abandoned or unexpected service " + name);
-                }
-            }
-        }
-    };
-
-    // Schedules a timeout / end-of-work based on the task verb
-    void scheduleOpTimeoutTm(ActiveTask task) {
-        final long timeoutMillis = (task.verb == VERB_IDLING) ? IDLE_TIMESLICE : OP_TIMEOUT;
-        if (DEBUG) {
-            Slog.i(TAG, "Scheduling timeout (token " + task.token
-                    + " : verb " + task.verb + ") for " + task + " in " + timeoutMillis);
-        }
-        mPendingOperations.put(task.token, task);
-        mHandler.removeMessages(MSG_TIMEOUT);
-        final Message msg = mHandler.obtainMessage(MSG_TIMEOUT, 0, task.token);
-        mHandler.sendMessageDelayed(msg, timeoutMillis);
-    }
-
-    // -------------------------------------------------------------------------------
-    public IdleMaintenanceService(Context context, BatteryService batteryService) {
-        mContext = context;
-        mBatteryService = batteryService;
-
-        mAlarmService = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
-
-        PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
-        mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
-        mHandler = new IdleHandler(mContext.getMainLooper());
-        mCallback = new IdleCallback();
-
-        Intent intent = new Intent(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
-        intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
-        mUpdateIdleMaintenanceStatePendingIntent = PendingIntent.getBroadcast(mContext, 0,
-                intent, PendingIntent.FLAG_UPDATE_CURRENT);
-
-        register(mHandler);
-    }
-
-    public void register(Handler handler) {
-        IntentFilter intentFilter = new IntentFilter();
-
-        // Alarm actions.
-        intentFilter.addAction(ACTION_UPDATE_IDLE_MAINTENANCE_STATE);
-
-        // Battery actions.
-        intentFilter.addAction(Intent.ACTION_BATTERY_CHANGED);
-
-        // Screen actions.
-        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
-        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
-
-        // Dream actions.
-        intentFilter.addAction(Intent.ACTION_DREAMING_STARTED);
-        intentFilter.addAction(Intent.ACTION_DREAMING_STOPPED);
-
-        mContext.registerReceiverAsUser(this, UserHandle.ALL,
-                intentFilter, null, mHandler);
-
-        intentFilter = new IntentFilter();
-        intentFilter.addAction(ACTION_FORCE_IDLE_MAINTENANCE);
-        mContext.registerReceiverAsUser(this, UserHandle.ALL,
-                intentFilter, android.Manifest.permission.SET_ACTIVITY_WATCHER, mHandler);
-    }
-
-    private void scheduleUpdateIdleMaintenanceState(long delayMillis) {
-        final long triggetRealTimeMillis = SystemClock.elapsedRealtime() + delayMillis;
-        mAlarmService.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggetRealTimeMillis,
-                mUpdateIdleMaintenanceStatePendingIntent);
-    }
-
-    private void unscheduleUpdateIdleMaintenanceState() {
-        mAlarmService.cancel(mUpdateIdleMaintenanceStatePendingIntent);
-    }
-
-    private void updateIdleMaintenanceStateTm(boolean noisy) {
-        if (mIdleMaintenanceStarted) {
-            // Idle maintenance can be interrupted by user activity, or duration
-            // time out, or low battery.
-            final boolean batteryOk
-                    = batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning();
-            if (!lastUserActivityPermitsIdleMaintenanceRunning() || !batteryOk) {
-                unscheduleUpdateIdleMaintenanceState();
-                mIdleMaintenanceStarted = false;
-                // We stopped since we don't have enough battery or timed out but the
-                // user is not using the device, so we should be able to run maintenance
-                // in the next maintenance window since the battery may be charged
-                // without interaction and the min interval between maintenances passed.
-                if (!batteryOk) {
-                    scheduleUpdateIdleMaintenanceState(
-                            getNextIdleMaintenanceIntervalStartFromNow());
-                }
-
-                EventLogTags.writeIdleMaintenanceWindowFinish(SystemClock.elapsedRealtime(),
-                        mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
-                        isBatteryCharging() ? 1 : 0);
-                scheduleIdleFinishTm();
-            }
-        } else if (deviceStatePermitsIdleMaintenanceStart(noisy)
-                && lastUserActivityPermitsIdleMaintenanceStart(noisy)
-                && lastRunPermitsIdleMaintenanceStart(noisy)) {
-            // Now that we started idle maintenance, we should schedule another
-            // update for the moment when the idle maintenance times out.
-            scheduleUpdateIdleMaintenanceState(MAX_IDLE_MAINTENANCE_DURATION);
-            mIdleMaintenanceStarted = true;
-            EventLogTags.writeIdleMaintenanceWindowStart(SystemClock.elapsedRealtime(),
-                    mLastUserActivityElapsedTimeMillis, mBatteryService.getBatteryLevel(),
-                    isBatteryCharging() ? 1 : 0);
-            mLastIdleMaintenanceStartTimeMillis = SystemClock.elapsedRealtime();
-            startIdleMaintenanceTm();
-        } else if (lastUserActivityPermitsIdleMaintenanceStart(noisy)) {
-             if (lastRunPermitsIdleMaintenanceStart(noisy)) {
-                // The user does not use the device and we did not run maintenance in more
-                // than the min interval between runs, so schedule an update - maybe the
-                // battery will be charged latter.
-                scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
-             } else {
-                 // The user does not use the device but we have run maintenance in the min
-                 // interval between runs, so schedule an update after the min interval ends.
-                 scheduleUpdateIdleMaintenanceState(
-                         getNextIdleMaintenanceIntervalStartFromNow());
-             }
-        }
-    }
-
-    void startIdleMaintenanceTm() {
-        if (DEBUG) {
-            Slog.i(TAG, "*** Starting idle maintenance ***");
-        }
-        if (DEBUG) { Slog.i(TAG, "Attributing wakelock to system work source"); }
-        mWakeLock.setWorkSource(mSystemWorkSource);
-        mWakeLock.acquire();
-        updateIdleServiceQueueTm();
-        mCurrentIdler = null;
-        mLastIdler = (mIdleServiceQueue.size() > 0) ? mIdleServiceQueue.peekLast() : null;
-        startNextIdleServiceTm();
-    }
-
-    // Start a graceful wind-down of the idle maintenance state: end the current idler
-    // and pretend that we've finished running the queue.  If there's no current idler,
-    // this is a no-op.
-    void scheduleIdleFinishTm() {
-        if (mCurrentIdler != null) {
-            if (DEBUG) {
-                Slog.i(TAG, "*** Finishing idle maintenance ***");
-            }
-            mLastIdler = mCurrentIdler;
-            sendEndIdleTm(mCurrentIdler);
-        } else {
-            if (DEBUG) {
-                Slog.w(TAG, "Asked to finish idle maintenance but we're done already");
-            }
-        }
-    }
-
-    // Actual finalization of the idle maintenance sequence
-    void stopIdleMaintenanceTm() {
-        if (mLastIdler != null) {
-            if (DEBUG) {
-                Slog.i(TAG, "*** Idle maintenance shutdown ***");
-            }
-            mWakeLock.setWorkSource(mSystemWorkSource);
-            mLastIdler = mCurrentIdler = null;
-            updateIdleMaintenanceStateTm(false);   // resets 'started' and schedules next window
-            mWakeLock.release();
-        } else {
-            Slog.e(TAG, "ERROR: idle shutdown but invariants not held.  last=" + mLastIdler
-                    + " cur=" + mCurrentIdler + " size=" + mIdleServiceQueue.size());
-        }
-    }
-
-    private long getNextIdleMaintenanceIntervalStartFromNow() {
-        return mLastIdleMaintenanceStartTimeMillis + MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS
-                - SystemClock.elapsedRealtime();
-    }
-
-    private boolean deviceStatePermitsIdleMaintenanceStart(boolean noisy) {
-        final int minBatteryLevel = isBatteryCharging()
-                ? MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_CHARGING
-                : MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_START_NOT_CHARGING;
-        boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
-                && mBatteryService.getBatteryLevel() > minBatteryLevel);
-        if (!allowed && noisy) {
-            Slog.i("IdleMaintenance", "Idle maintenance not allowed due to power");
-        }
-        return allowed;
-    }
-
-    private boolean lastUserActivityPermitsIdleMaintenanceStart(boolean noisy) {
-        // The last time the user poked the device is above the threshold.
-        boolean allowed = (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID
-                && SystemClock.elapsedRealtime() - mLastUserActivityElapsedTimeMillis
-                    > MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
-        if (!allowed && noisy) {
-            Slog.i("IdleMaintenance", "Idle maintenance not allowed due to last user activity");
-        }
-        return allowed;
-    }
-
-    private boolean lastRunPermitsIdleMaintenanceStart(boolean noisy) {
-        // Enough time passed since the last maintenance run.
-        boolean allowed = SystemClock.elapsedRealtime() - mLastIdleMaintenanceStartTimeMillis
-                > MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
-        if (!allowed && noisy) {
-            Slog.i("IdleMaintenance", "Idle maintenance not allowed due time since last");
-        }
-        return allowed;
-    }
-
-    private boolean lastUserActivityPermitsIdleMaintenanceRunning() {
-        // The user is not using the device.
-        return (mLastUserActivityElapsedTimeMillis != LAST_USER_ACTIVITY_TIME_INVALID);
-    }
-
-    private boolean batteryLevelAndMaintenanceTimeoutPermitsIdleMaintenanceRunning() {
-        // Battery not too low and the maintenance duration did not timeout.
-        return (mBatteryService.getBatteryLevel() > MIN_BATTERY_LEVEL_IDLE_MAINTENANCE_RUNNING
-                && mLastIdleMaintenanceStartTimeMillis + MAX_IDLE_MAINTENANCE_DURATION
-                        > SystemClock.elapsedRealtime());
-    }
-
-    private boolean isBatteryCharging() {
-        return mBatteryService.getPlugType() > 0
-                && mBatteryService.getInvalidCharger() == 0;
-    }
-
-    @Override
-    public void onReceive(Context context, Intent intent) {
-        if (DEBUG) {
-            Log.i(TAG, intent.getAction());
-        }
-        String action = intent.getAction();
-        if (Intent.ACTION_BATTERY_CHANGED.equals(action)) {
-            // We care about battery only if maintenance is in progress so we can
-            // stop it if battery is too low. Note that here we assume that the
-            // maintenance clients are properly holding a wake lock. We will
-            // refactor the maintenance to use services instead of intents for the
-            // next release. The only client for this for now is internal an holds
-            // a wake lock correctly.
-            if (mIdleMaintenanceStarted) {
-                updateIdleMaintenanceStateTm(false);
-            }
-        } else if (Intent.ACTION_SCREEN_ON.equals(action)
-                || Intent.ACTION_DREAMING_STOPPED.equals(action)) {
-            mLastUserActivityElapsedTimeMillis = LAST_USER_ACTIVITY_TIME_INVALID;
-            // Unschedule any future updates since we already know that maintenance
-            // cannot be performed since the user is back.
-            unscheduleUpdateIdleMaintenanceState();
-            // If the screen went on/stopped dreaming, we know the user is using the
-            // device which means that idle maintenance should be stopped if running.
-            updateIdleMaintenanceStateTm(false);
-        } else if (Intent.ACTION_SCREEN_OFF.equals(action)
-                || Intent.ACTION_DREAMING_STARTED.equals(action)) {
-            mLastUserActivityElapsedTimeMillis = SystemClock.elapsedRealtime();
-            // If screen went off/started dreaming, we may be able to start idle maintenance
-            // after the minimal user inactivity elapses. We schedule an alarm for when
-            // this timeout elapses since the device may go to sleep by then.
-            scheduleUpdateIdleMaintenanceState(MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START);
-        } else if (ACTION_UPDATE_IDLE_MAINTENANCE_STATE.equals(action)) {
-            updateIdleMaintenanceStateTm(false);
-        } else if (ACTION_FORCE_IDLE_MAINTENANCE.equals(action)) {
-            long now = SystemClock.elapsedRealtime() - 1;
-            mLastUserActivityElapsedTimeMillis = now - MIN_USER_INACTIVITY_IDLE_MAINTENANCE_START;
-            mLastIdleMaintenanceStartTimeMillis = now - MIN_IDLE_MAINTENANCE_INTERVAL_MILLIS;
-            updateIdleMaintenanceStateTm(true);
-        }
-    }
-}
diff --git a/services/core/java/com/android/server/MountServiceIdler.java b/services/core/java/com/android/server/MountServiceIdler.java
index bcb6e9e..215d92d 100644
--- a/services/core/java/com/android/server/MountServiceIdler.java
+++ b/services/core/java/com/android/server/MountServiceIdler.java
@@ -30,8 +30,7 @@
     private static final String TAG = "MountServiceIdler";
 
     private static ComponentName sIdleService =
-            new ComponentName(MountServiceIdler.class.getPackage().getName(),
-                    MountServiceIdler.class.getName());
+            new ComponentName("android", MountServiceIdler.class.getName());
 
     private static int MOUNT_JOB_ID = 808;
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 7022294..f9c7a78 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -63,6 +63,7 @@
 import android.telephony.DataConnectionRealTimeInfo;
 import android.telephony.PhoneStateListener;
 import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseBooleanArray;
@@ -240,15 +241,22 @@
         mThread = new Thread(mConnector, NETD_TAG);
 
         mDaemonHandler = new Handler(FgThread.get().getLooper());
-        mPhoneStateListener = new PhoneStateListener(
-                SubscriptionManager.DEFAULT_SUB_ID, // FIXME: What Subscription should be used??
+
+        mPhoneStateListener = new PhoneStateListener(SubscriptionManager.DEFAULT_SUB_ID,
                 mDaemonHandler.getLooper()) {
+            @Override
             public void onDataConnectionRealTimeInfoChanged(
                     DataConnectionRealTimeInfo dcRtInfo) {
+                if (DBG) Slog.d(TAG, "onDataConnectionRealTimeInfoChanged: " + dcRtInfo);
                 notifyInterfaceClassActivity(ConnectivityManager.TYPE_MOBILE,
                         dcRtInfo.getDcPowerState(), dcRtInfo.getTime(), true);
             }
         };
+        TelephonyManager tm = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
+        if (tm != null) {
+            tm.listen(mPhoneStateListener,
+                    PhoneStateListener.LISTEN_DATA_CONNECTION_REAL_TIME_INFO);
+        }
 
         // Add ourself to the Watchdog monitors.
         Watchdog.getInstance().addMonitor(this);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 9d92421..1bd837b 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -89,7 +89,7 @@
 
         @Override
         public String toString() {
-            return "{pkgForDebug=" + pkgForDebug + " callerUid=" + callerUid +
+            return "{pkgForDebug=" + pkgForDebug + " callerUid=" + callerUid + " subId=" + subId +
                     " events=" + Integer.toHexString(events) + "}";
         }
     }
@@ -208,11 +208,13 @@
             String action = intent.getAction();
             Slog.d(TAG, "mBroadcastReceiver: action=" + action);
             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED,
-                       intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0));
+                int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
+                if (DBG) Slog.d(TAG, "onReceive: userHandle=" + userHandle);
+                mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, userHandle, 0));
             } else if (action.equals(TelephonyIntents.ACTION_DEFAULT_SUBSCRIPTION_CHANGED)) {
                 mDefaultSubId = intent.getLongExtra(PhoneConstants.SUBSCRIPTION_KEY,
                         SubscriptionManager.getDefaultSubId());
+                if (DBG) Slog.d(TAG, "onReceive: mDefaultSubId=" + mDefaultSubId);
                 mHandler.sendMessage(mHandler.obtainMessage(MSG_UPDATE_DEFAULT_SUB, 0, 0));
             }
         }
@@ -340,18 +342,19 @@
                     // the received subId value update the isLegacyApp field
                     if ((r.subId <= 0) || (r.subId == SubscriptionManager.INVALID_SUB_ID)) {
                         r.subId = mDefaultSubId;
-                        r.isLegacyApp = true; // FIXME: is this needed ??
+                        r.isLegacyApp = true; // r.subId is to be update when default changes.
                     }
                     if (r.subId == SubscriptionManager.DEFAULT_SUB_ID) {
                         r.subId = mDefaultSubId;
+                        r.isLegacyApp = true; // r.subId is to be update when default changes.
                         if (DBG) Slog.i(TAG, "listen: DEFAULT_SUB_ID");
                     }
                     mRecords.add(r);
-                    if (DBG) Slog.i(TAG, "listen: add new record=" + r);
+                    if (DBG) Slog.i(TAG, "listen: add new record");
                 }
                 int phoneId = SubscriptionManager.getPhoneId(subId);
-                int send = events & (events ^ r.events);
                 r.events = events;
+                if (DBG) Slog.i(TAG, "listen: set events record=" + r);
                 if (notifyNow && validatePhoneId(phoneId)) {
                     if ((events & PhoneStateListener.LISTEN_SERVICE_STATE) != 0) {
                         try {
@@ -1063,6 +1066,7 @@
             pw.println("  mDataConnectionLinkProperties=" + mDataConnectionLinkProperties);
             pw.println("  mDataConnectionNetworkCapabilities=" +
                     mDataConnectionNetworkCapabilities);
+            pw.println("  mDefaultSubId=" + mDefaultSubId);
             pw.println("  mCellLocation=" + mCellLocation);
             pw.println("  mCellInfo=" + mCellInfo);
             pw.println("  mDcRtInfo=" + mDcRtInfo);
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 816e022..69262c8 100755
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1328,7 +1328,7 @@
                         + " app=" + app);
             if (app != null && app.thread != null) {
                 try {
-                    app.addPackage(r.appInfo.packageName, mAm.mProcessStats);
+                    app.addPackage(r.appInfo.packageName, r.appInfo.versionCode, mAm.mProcessStats);
                     realStartServiceLocked(r, app, execInFg);
                     return null;
                 } catch (RemoteException e) {
@@ -1883,7 +1883,8 @@
 
                     mPendingServices.remove(i);
                     i--;
-                    proc.addPackage(sr.appInfo.packageName, mAm.mProcessStats);
+                    proc.addPackage(sr.appInfo.packageName, sr.appInfo.versionCode,
+                            mAm.mProcessStats);
                     realStartServiceLocked(sr, proc, sr.createdFromFg);
                     didSomething = true;
                 }
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 697e1f2..3bff2ad 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -36,6 +36,9 @@
 import android.app.IAppTask;
 import android.app.admin.DevicePolicyManager;
 import android.appwidget.AppWidgetManager;
+import android.content.DialogInterface.OnClickListener;
+import android.content.res.Resources;
+import android.graphics.BitmapFactory;
 import android.graphics.Rect;
 import android.os.BatteryStats;
 import android.os.PersistableBundle;
@@ -171,8 +174,12 @@
 import android.os.UpdateLock;
 import android.os.UserHandle;
 import android.provider.Settings;
+import android.text.Spannable;
+import android.text.SpannableString;
 import android.text.format.DateUtils;
 import android.text.format.Time;
+import android.text.style.DynamicDrawableSpan;
+import android.text.style.ImageSpan;
 import android.util.AtomicFile;
 import android.util.EventLog;
 import android.util.Log;
@@ -186,6 +193,7 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
+import android.widget.TextView;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
@@ -570,6 +578,12 @@
     long mLastFullPssTime = SystemClock.uptimeMillis();
 
     /**
+     * If set, the next time we collect PSS data we should do a full collection
+     * with data from native processes and the kernel.
+     */
+    boolean mFullPssPending = false;
+
+    /**
      * This is the process holding what we currently consider to be
      * the "home" activity.
      */
@@ -1158,6 +1172,8 @@
     CompatModeDialog mCompatModeDialog;
     long mLastMemUsageReportTime = 0;
 
+    private LockToAppRequestDialog mLockToAppRequest;
+
     /**
      * Flag whether the current user is a "monkey", i.e. whether
      * the UI is driven by a UI automation tool.
@@ -1801,8 +1817,47 @@
         public void handleMessage(Message msg) {
             switch (msg.what) {
             case COLLECT_PSS_BG_MSG: {
-                int i=0, num=0;
                 long start = SystemClock.uptimeMillis();
+                MemInfoReader memInfo = null;
+                synchronized (ActivityManagerService.this) {
+                    if (mFullPssPending) {
+                        mFullPssPending = false;
+                        memInfo = new MemInfoReader();
+                    }
+                }
+                if (memInfo != null) {
+                    updateCpuStatsNow();
+                    long nativeTotalPss = 0;
+                    synchronized (mProcessCpuThread) {
+                        final int N = mProcessCpuTracker.countStats();
+                        for (int j=0; j<N; j++) {
+                            ProcessCpuTracker.Stats st = mProcessCpuTracker.getStats(j);
+                            if (st.vsize <= 0 || st.uid >= Process.FIRST_APPLICATION_UID) {
+                                // This is definitely an application process; skip it.
+                                continue;
+                            }
+                            synchronized (mPidsSelfLocked) {
+                                if (mPidsSelfLocked.indexOfKey(st.pid) >= 0) {
+                                    // This is one of our own processes; skip it.
+                                    continue;
+                                }
+                            }
+                            nativeTotalPss += Debug.getPss(st.pid, null);
+                        }
+                    }
+                    memInfo.readMemInfo();
+                    synchronized (this) {
+                        if (DEBUG_PSS) Slog.d(TAG, "Collected native and kernel memory in "
+                                + (SystemClock.uptimeMillis()-start) + "ms");
+                        mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
+                                memInfo.getFreeSizeKb(), memInfo.getZramTotalSizeKb(),
+                                memInfo.getBuffersSizeKb()+memInfo.getShmemSizeKb()
+                                        +memInfo.getSlabSizeKb(),
+                                nativeTotalPss);
+                    }
+                }
+
+                int i=0, num=0;
                 long[] tmp = new long[1];
                 do {
                     ProcessRecord proc;
@@ -2176,6 +2231,8 @@
             }
         };
 
+        mLockToAppRequest = new LockToAppRequestDialog(mContext, this);
+
         Watchdog.getInstance().addMonitor(this);
         Watchdog.getInstance().addThread(mHandler);
     }
@@ -2194,6 +2251,11 @@
         LocalServices.addService(ActivityManagerInternal.class, new LocalService());
     }
 
+    public void initPowerManagement() {
+        mStackSupervisor.initPowerManagement();
+        mBatteryStatsService.initPowerManagement();
+    }
+
     @Override
     public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
             throws RemoteException {
@@ -2764,7 +2826,7 @@
                 // come up (we have a pid but not yet its thread), so keep it.
                 if (DEBUG_PROCESSES) Slog.v(TAG, "App already running: " + app);
                 // If this is a new package in the process, add the package to the list
-                app.addPackage(info.packageName, mProcessStats);
+                app.addPackage(info.packageName, info.versionCode, mProcessStats);
                 return app;
             }
 
@@ -2819,7 +2881,7 @@
             }
         } else {
             // If this is a new package in the process, add the package to the list
-            app.addPackage(info.packageName, mProcessStats);
+            app.addPackage(info.packageName, info.versionCode, mProcessStats);
         }
 
         // If the system is not ready yet, then hold off on starting this
@@ -3657,6 +3719,12 @@
             // Keep track of the root activity of the task before we finish it
             TaskRecord tr = r.task;
             ActivityRecord rootR = tr.getRootActivity();
+            // Do not allow task to finish in Lock Task mode.
+            if (tr == mStackSupervisor.mLockTaskModeTask) {
+                if (rootR == r) {
+                    return false;
+                }
+            }
             if (mController != null) {
                 // Find the first activity that is not finishing.
                 ActivityRecord next = r.task.stack.topRunningActivityLocked(token, 0);
@@ -3801,13 +3869,25 @@
     public boolean finishActivityAffinity(IBinder token) {
         synchronized(this) {
             final long origId = Binder.clearCallingIdentity();
-            ActivityRecord r = ActivityRecord.isInStackLocked(token);
-            boolean res = false;
-            if (r != null) {
-                res = r.task.stack.finishActivityAffinityLocked(r);
+            try {
+                ActivityRecord r = ActivityRecord.isInStackLocked(token);
+
+                ActivityRecord rootR = r.task.getRootActivity();
+                // Do not allow task to finish in Lock Task mode.
+                if (r.task == mStackSupervisor.mLockTaskModeTask) {
+                    if (rootR == r) {
+                        Binder.restoreCallingIdentity(origId);
+                        return false;
+                    }
+                }
+                boolean res = false;
+                if (r != null) {
+                    res = r.task.stack.finishActivityAffinityLocked(r);
+                }
+                return res;
+            } finally {
+                Binder.restoreCallingIdentity(origId);
             }
-            Binder.restoreCallingIdentity(origId);
-            return res;
         }
     }
 
@@ -6039,7 +6119,11 @@
         if (UserHandle.getUserId(uid) != grantUri.sourceUserId) {
             return false;
         }
+        return checkHoldingPermissionsInternalLocked(pm, pi, grantUri, uid, modeFlags, true);
+    }
 
+    private final boolean checkHoldingPermissionsInternalLocked(IPackageManager pm, ProviderInfo pi,
+            GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) {
         if (pi.applicationInfo.uid == uid) {
             return true;
         } else if (!pi.exported) {
@@ -6050,11 +6134,11 @@
         boolean writeMet = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0;
         try {
             // check if target holds top-level <provider> permissions
-            if (!readMet && pi.readPermission != null
+            if (!readMet && pi.readPermission != null && considerUidPermissions
                     && (pm.checkUidPermission(pi.readPermission, uid) == PERMISSION_GRANTED)) {
                 readMet = true;
             }
-            if (!writeMet && pi.writePermission != null
+            if (!writeMet && pi.writePermission != null && considerUidPermissions
                     && (pm.checkUidPermission(pi.writePermission, uid) == PERMISSION_GRANTED)) {
                 writeMet = true;
             }
@@ -6080,7 +6164,8 @@
                                     + ": match=" + pp.match(path)
                                     + " check=" + pm.checkUidPermission(pprperm, uid));
                             if (pprperm != null) {
-                                if (pm.checkUidPermission(pprperm, uid) == PERMISSION_GRANTED) {
+                                if (considerUidPermissions && pm.checkUidPermission(pprperm, uid)
+                                        == PERMISSION_GRANTED) {
                                     readMet = true;
                                 } else {
                                     allowDefaultRead = false;
@@ -6094,7 +6179,8 @@
                                     + ": match=" + pp.match(path)
                                     + " check=" + pm.checkUidPermission(ppwperm, uid));
                             if (ppwperm != null) {
-                                if (pm.checkUidPermission(ppwperm, uid) == PERMISSION_GRANTED) {
+                                if (considerUidPermissions && pm.checkUidPermission(ppwperm, uid)
+                                        == PERMISSION_GRANTED) {
                                     writeMet = true;
                                 } else {
                                     allowDefaultWrite = false;
@@ -6291,28 +6377,40 @@
             }
         }
 
+        /* There is a special cross user grant if:
+         * - The target is on another user.
+         * - Apps on the current user can access the uri without any uid permissions.
+         * In this case, we grant a uri permission, even if the ContentProvider does not normally
+         * grant uri permissions.
+         */
+        boolean specialCrossUserGrant = UserHandle.getUserId(targetUid) != grantUri.sourceUserId
+                && checkHoldingPermissionsInternalLocked(pm, pi, grantUri, callingUid,
+                modeFlags, false /*without considering the uid permissions*/);
+
         // Second...  is the provider allowing granting of URI permissions?
-        if (!pi.grantUriPermissions) {
-            throw new SecurityException("Provider " + pi.packageName
-                    + "/" + pi.name
-                    + " does not allow granting of Uri permissions (uri "
-                    + grantUri + ")");
-        }
-        if (pi.uriPermissionPatterns != null) {
-            final int N = pi.uriPermissionPatterns.length;
-            boolean allowed = false;
-            for (int i=0; i<N; i++) {
-                if (pi.uriPermissionPatterns[i] != null
-                        && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
-                    allowed = true;
-                    break;
-                }
-            }
-            if (!allowed) {
+        if (!specialCrossUserGrant) {
+            if (!pi.grantUriPermissions) {
                 throw new SecurityException("Provider " + pi.packageName
                         + "/" + pi.name
-                        + " does not allow granting of permission to path of Uri "
-                        + grantUri);
+                        + " does not allow granting of Uri permissions (uri "
+                        + grantUri + ")");
+            }
+            if (pi.uriPermissionPatterns != null) {
+                final int N = pi.uriPermissionPatterns.length;
+                boolean allowed = false;
+                for (int i=0; i<N; i++) {
+                    if (pi.uriPermissionPatterns[i] != null
+                            && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) {
+                        allowed = true;
+                        break;
+                    }
+                }
+                if (!allowed) {
+                    throw new SecurityException("Provider " + pi.packageName
+                            + "/" + pi.name
+                            + " does not allow granting of permission to path of Uri "
+                            + grantUri);
+                }
             }
         }
 
@@ -7130,7 +7228,7 @@
         // Compose the recent task info
         ActivityManager.RecentTaskInfo rti
                 = new ActivityManager.RecentTaskInfo();
-        rti.id = tr.mActivities.isEmpty() ? -1 : tr.taskId;
+        rti.id = tr.getTopActivity() == null ? -1 : tr.taskId;
         rti.persistentId = tr.taskId;
         rti.baseIntent = new Intent(tr.getBaseIntent());
         rti.origActivity = tr.origActivity;
@@ -7197,6 +7295,12 @@
                             continue;
                         }
                     }
+                    if (tr.intent != null &&
+                            (tr.intent.getFlags() & Intent.FLAG_ACTIVITY_AUTO_REMOVE_FROM_RECENTS)
+                            != 0 && tr.getTopActivity() == null) {
+                        // Don't include auto remove tasks that are finished or finishing.
+                        continue;
+                    }
 
                     ActivityManager.RecentTaskInfo rti = createRecentTaskInfoFromTaskRecord(tr);
                     if (!detailed) {
@@ -7416,6 +7520,10 @@
                     Slog.e(TAG, "moveTaskToFront: Attempt to violate Lock Task Mode");
                     return;
                 }
+                final ActivityRecord prev = mStackSupervisor.topRunningActivityLocked();
+                if (prev != null && prev.isRecentsActivity()) {
+                    task.setTaskToReturnTo(ActivityRecord.RECENTS_ACTIVITY_TYPE);
+                }
                 mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options);
             } finally {
                 Binder.restoreCallingIdentity(origId);
@@ -7625,14 +7733,32 @@
         }
     }
 
-    private boolean isLockTaskAuthorized(ComponentName name) {
+    private boolean isLockTaskAuthorized(String pkg) {
         final DevicePolicyManager dpm = (DevicePolicyManager)
                 mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
-        return dpm != null && dpm.isLockTaskPermitted(name);
+        try {
+            int uid = mContext.getPackageManager().getPackageUid(pkg,
+                    Binder.getCallingUserHandle().getIdentifier());
+            return (uid == Binder.getCallingUid()) && dpm != null && dpm.isLockTaskPermitted(pkg);
+        } catch (NameNotFoundException e) {
+            return false;
+        }
     }
 
-    private void startLockTaskMode(TaskRecord task) {
-        if (!isLockTaskAuthorized(task.intent.getComponent())) {
+    void startLockTaskMode(TaskRecord task) {
+        final String pkg;
+        synchronized (this) {
+            pkg = task.intent.getComponent().getPackageName();
+        }
+        boolean isSystemInitiated = Binder.getCallingUid() == Process.SYSTEM_UID;
+        if (!isSystemInitiated && !isLockTaskAuthorized(pkg)) {
+            final TaskRecord taskRecord = task;
+            mHandler.post(new Runnable() {
+                @Override
+                public void run() {
+                    mLockToAppRequest.showLockTaskPrompt(taskRecord);
+                }
+            });
             return;
         }
         long ident = Binder.clearCallingIdentity();
@@ -7641,7 +7767,10 @@
                 // Since we lost lock on task, make sure it is still there.
                 task = mStackSupervisor.anyTaskForIdLocked(task.taskId);
                 if (task != null) {
-                    mStackSupervisor.setLockTaskModeLocked(task);
+                    if ((mFocusedActivity == null) || (task != mFocusedActivity.task)) {
+                        throw new IllegalArgumentException("Invalid task, not in foreground");
+                    }
+                    mStackSupervisor.setLockTaskModeLocked(task, isSystemInitiated);
                 }
             }
         } finally {
@@ -7651,25 +7780,25 @@
 
     @Override
     public void startLockTaskMode(int taskId) {
+        final TaskRecord task;
         long ident = Binder.clearCallingIdentity();
         try {
-            final TaskRecord task;
             synchronized (this) {
                 task = mStackSupervisor.anyTaskForIdLocked(taskId);
             }
-            if (task != null) {
-                startLockTaskMode(task);
-            }
         } finally {
             Binder.restoreCallingIdentity(ident);
         }
+        if (task != null) {
+            startLockTaskMode(task);
+        }
     }
 
     @Override
     public void startLockTaskMode(IBinder token) {
+        final TaskRecord task;
         long ident = Binder.clearCallingIdentity();
         try {
-            final TaskRecord task;
             synchronized (this) {
                 final ActivityRecord r = ActivityRecord.forToken(token);
                 if (r == null) {
@@ -7677,8 +7806,50 @@
                 }
                 task = r.task;
             }
-            if (task != null) {
-                startLockTaskMode(task);
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+        if (task != null) {
+            startLockTaskMode(task);
+        }
+    }
+
+    @Override
+    public void startLockTaskModeOnCurrent() throws RemoteException {
+        checkCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS);
+        ActivityRecord r = null;
+        synchronized (this) {
+            r = mStackSupervisor.topRunningActivityLocked();
+        }
+        startLockTaskMode(r.task);
+    }
+
+    @Override
+    public void stopLockTaskMode() {
+        // Verify that the user matches the package of the intent for the TaskRecord
+        // we are locked to or systtem.  This will ensure the same caller for startLockTaskMode
+        // and stopLockTaskMode.
+        final int callingUid = Binder.getCallingUid();
+        if (callingUid != Process.SYSTEM_UID) {
+            try {
+                String pkg =
+                        mStackSupervisor.mLockTaskModeTask.intent.getComponent().getPackageName();
+                int uid = mContext.getPackageManager().getPackageUid(pkg,
+                        Binder.getCallingUserHandle().getIdentifier());
+                if (uid != callingUid) {
+                    throw new SecurityException("Invalid uid, expected " + uid);
+                }
+            } catch (NameNotFoundException e) {
+                Log.d(TAG, "stopLockTaskMode " + e);
+                return;
+            }
+        }
+        long ident = Binder.clearCallingIdentity();
+        try {
+            Log.d(TAG, "stopLockTaskMode");
+            // Stop lock task
+            synchronized (this) {
+                mStackSupervisor.setLockTaskModeLocked(null, false);
             }
         } finally {
             Binder.restoreCallingIdentity(ident);
@@ -7686,21 +7857,13 @@
     }
 
     @Override
-    public void stopLockTaskMode() {
-        // Check if the calling task is eligible to use lock task
-        final int uid = Binder.getCallingUid();
+    public void stopLockTaskModeOnCurrent() throws RemoteException {
+        checkCallingPermission(android.Manifest.permission.MANAGE_ACTIVITY_STACKS);
+        long ident = Binder.clearCallingIdentity();
         try {
-            final String name = AppGlobals.getPackageManager().getNameForUid(uid);
-            if (!isLockTaskAuthorized(new ComponentName(name, name))) {
-                return;
-            }
-        } catch (RemoteException e) {
-            Log.d(TAG, "stopLockTaskMode " + e);
-            return;
-        }
-        // Stop lock task
-        synchronized (this) {
-            mStackSupervisor.setLockTaskModeLocked(null);
+            stopLockTaskMode();
+        } finally {
+            Binder.restoreCallingIdentity(ident);
         }
     }
 
@@ -7759,7 +7922,8 @@
                     // to run in multiple processes, because this is actually
                     // part of the framework so doesn't make sense to track as a
                     // separate apk in the process.
-                    app.addPackage(cpi.applicationInfo.packageName, mProcessStats);
+                    app.addPackage(cpi.applicationInfo.packageName, cpi.applicationInfo.versionCode,
+                            mProcessStats);
                 }
                 ensurePackageDexOpt(cpi.applicationInfo.packageName);
             }
@@ -12567,6 +12731,8 @@
             }
         }
 
+        long nativeProcTotalPss = 0;
+
         if (!isCheckinRequest && procs.size() > 1) {
             // If we are showing aggregations, also look for native processes to
             // include so that our aggregations are more accurate.
@@ -12588,6 +12754,7 @@
 
                         final long myTotalPss = mi.getTotalPss();
                         totalPss += myTotalPss;
+                        nativeProcTotalPss += myTotalPss;
 
                         MemItem pssItem = new MemItem(st.name + " (pid " + st.pid + ")",
                                 st.name, myTotalPss, st.pid, false);
@@ -12655,6 +12822,14 @@
             }
             MemInfoReader memInfo = new MemInfoReader();
             memInfo.readMemInfo();
+            if (nativeProcTotalPss > 0) {
+                synchronized (this) {
+                    mProcessStats.addSysMemUsageLocked(memInfo.getCachedSizeKb(),
+                            memInfo.getFreeSizeKb(), memInfo.getZramTotalSizeKb(),
+                            memInfo.getBuffersSizeKb()+memInfo.getShmemSizeKb()+memInfo.getSlabSizeKb(),
+                            nativeProcTotalPss);
+                }
+            }
             if (!brief) {
                 if (!isCompact) {
                     pw.print("Total RAM: "); pw.print(memInfo.getTotalSizeKb());
@@ -15435,6 +15610,7 @@
         }
         if (DEBUG_PSS) Slog.d(TAG, "Requesting PSS of all procs!  memLowered=" + memLowered);
         mLastFullPssTime = now;
+        mFullPssPending = true;
         mPendingPssProcesses.ensureCapacity(mLruProcesses.size());
         mPendingPssProcesses.clear();
         for (int i=mLruProcesses.size()-1; i>=0; i--) {
@@ -16630,7 +16806,7 @@
                     return true;
                 }
 
-                mStackSupervisor.setLockTaskModeLocked(null);
+                mStackSupervisor.setLockTaskModeLocked(null, false);
 
                 final UserInfo userInfo = getUserManagerLocked().getUserInfo(userId);
                 if (userInfo == null) {
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 11f855e..0825f2e 100755
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -19,6 +19,7 @@
 import android.app.ActivityManager.TaskDescription;
 import android.os.PersistableBundle;
 import android.os.Trace;
+
 import com.android.internal.app.ResolverActivity;
 import com.android.internal.util.XmlUtils;
 import com.android.server.AttributeCache;
@@ -49,6 +50,7 @@
 import android.util.TimeUtils;
 import android.view.IApplicationToken;
 import android.view.WindowManager;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -58,6 +60,7 @@
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Objects;
 
 /**
  * An entry in the history stack, representing an activity.
@@ -83,6 +86,7 @@
     final ActivityManagerService service; // owner
     final IApplicationToken.Stub appToken; // window manager token
     final ActivityInfo info; // all about me
+    final ApplicationInfo appInfo; // information about activity's app
     final int launchedFromUid; // always the uid who started the activity.
     final String launchedFromPackage; // always the package who started the activity.
     final int userId;          // Which user is this running for?
@@ -103,9 +107,6 @@
     static final int RECENTS_ACTIVITY_TYPE = 2;
     int mActivityType;
 
-    final String baseDir;   // where activity source (resources etc) located
-    final String resDir;   // where public activity source (public resources etc) located
-    final String dataDir;   // where activity data should go
     CharSequence nonLocalizedLabel;  // the label information from the package mgr.
     int labelRes;           // the label information from the package mgr.
     int icon;               // resource identifier of activity's icon.
@@ -184,11 +185,13 @@
         pw.print(prefix); pw.print("taskAffinity="); pw.println(taskAffinity);
         pw.print(prefix); pw.print("realActivity=");
                 pw.println(realActivity.flattenToShortString());
-        pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
-        if (!resDir.equals(baseDir)) {
-            pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+        if (appInfo != null) {
+            pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
+            if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+                pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
+            }
+            pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
         }
-        pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
         pw.print(prefix); pw.print("stateNotNeeded="); pw.print(stateNotNeeded);
                 pw.print(" componentSpecified="); pw.print(componentSpecified);
                 pw.print(" mActivityType="); pw.println(mActivityType);
@@ -418,9 +421,7 @@
             taskAffinity = aInfo.taskAffinity;
             stateNotNeeded = (aInfo.flags&
                     ActivityInfo.FLAG_STATE_NOT_NEEDED) != 0;
-            baseDir = aInfo.applicationInfo.sourceDir;
-            resDir = aInfo.applicationInfo.publicSourceDir;
-            dataDir = aInfo.applicationInfo.dataDir;
+            appInfo = aInfo.applicationInfo;
             nonLocalizedLabel = aInfo.nonLocalizedLabel;
             labelRes = aInfo.labelRes;
             if (nonLocalizedLabel == null && labelRes == 0) {
@@ -488,9 +489,7 @@
             realActivity = null;
             taskAffinity = null;
             stateNotNeeded = false;
-            baseDir = null;
-            resDir = null;
-            dataDir = null;
+            appInfo = null;
             processName = null;
             packageName = null;
             fullscreen = true;
@@ -568,7 +567,10 @@
     }
 
     boolean isPersistable() {
-        return (info.flags & ActivityInfo.FLAG_PERSISTABLE) != 0;
+        return (info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY ||
+                info.persistableMode == ActivityInfo.PERSIST_ACROSS_REBOOTS) &&
+                (intent == null ||
+                        (intent.getFlags() & Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0);
     }
 
     void makeFinishing() {
@@ -580,11 +582,6 @@
         }
     }
 
-    boolean isRootActivity() {
-        final ArrayList<ActivityRecord> activities = task.mActivities;
-        return activities.size() == 0 || this == activities.get(0);
-    }
-
     UriPermissionOwner getUriPermissionsLocked() {
         if (uriPermissions == null) {
             uriPermissions = new UriPermissionOwner(service, this);
@@ -1033,11 +1030,11 @@
             return -1;
         }
         final TaskRecord task = r.task;
-        switch (task.mActivities.indexOf(r)) {
-            case -1: return -1;
-            case 0: return task.taskId;
-            default: return onlyRoot ? -1 : task.taskId;
+        final int activityNdx = task.mActivities.indexOf(r);
+        if (activityNdx < 0 || (onlyRoot && activityNdx > task.findEffectiveRootIndex())) {
+            return -1;
         }
+        return task.taskId;
     }
 
     static ActivityRecord isInStackLocked(IBinder token) {
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 03ce530..e1c7da9 100755
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -30,6 +30,10 @@
 import static com.android.server.am.ActivityManagerService.DEBUG_VISBILITY;
 import static com.android.server.am.ActivityManagerService.VALIDATE_TOKENS;
 
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
+
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_APP;
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_CONTAINERS;
@@ -1066,40 +1070,6 @@
         }
     }
 
-    /**
-     * Determine if home should be visible below the passed record.
-     * @param record activity we are querying for.
-     * @return true if home is visible below the passed activity, false otherwise.
-     */
-    boolean isActivityOverHome(ActivityRecord record) {
-        // Start at record and go down, look for either home or a visible fullscreen activity.
-        final TaskRecord recordTask = record.task;
-        for (int taskNdx = mTaskHistory.indexOf(recordTask); taskNdx >= 0; --taskNdx) {
-            TaskRecord task = mTaskHistory.get(taskNdx);
-            final ArrayList<ActivityRecord> activities = task.mActivities;
-            final int startNdx =
-                    task == recordTask ? activities.indexOf(record) : activities.size() - 1;
-            for (int activityNdx = startNdx; activityNdx >= 0; --activityNdx) {
-                final ActivityRecord r = activities.get(activityNdx);
-                if (r.isHomeActivity()) {
-                    return true;
-                }
-                if (!r.finishing && r.fullscreen) {
-                    // Passed activity is over a fullscreen activity.
-                    return false;
-                }
-            }
-            if (task.mOnTopOfHome) {
-                // Got to the bottom of a task on top of home without finding a visible fullscreen
-                // activity. Home is visible.
-                return true;
-            }
-        }
-        // Got to the bottom of this stack and still don't know. If this is over the home stack
-        // then record is over home. May not work if we ever get more than two layers.
-        return mStackSupervisor.isFrontStack(this);
-    }
-
     private void setVisibile(ActivityRecord r, boolean visible) {
         r.visible = visible;
         mWindowManager.setAppVisibility(r.appToken, visible);
@@ -1129,7 +1099,8 @@
         for (int i = mStacks.indexOf(this) + 1; i < mStacks.size(); i++) {
             final ArrayList<TaskRecord> tasks = mStacks.get(i).getAllTasks();
             for (int taskNdx = 0; taskNdx < tasks.size(); taskNdx++) {
-                final ArrayList<ActivityRecord> activities = tasks.get(taskNdx).mActivities;
+                final TaskRecord task = tasks.get(taskNdx);
+                final ArrayList<ActivityRecord> activities = task.mActivities;
                 for (int activityNdx = 0; activityNdx < activities.size(); activityNdx++) {
                     final ActivityRecord r = activities.get(activityNdx);
 
@@ -1140,7 +1111,7 @@
                     // - Full Screen Activity OR
                     // - On top of Home and our stack is NOT home
                     if (!r.finishing && r.visible && (r.fullscreen ||
-                            (!isHomeStack() && r.frontOfTask && tasks.get(taskNdx).mOnTopOfHome))) {
+                            (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()))) {
                         return false;
                     }
                 }
@@ -1268,7 +1239,7 @@
                         // At this point, nothing else needs to be shown
                         if (DEBUG_VISBILITY) Slog.v(TAG, "Fullscreen: at " + r);
                         behindFullscreen = true;
-                    } else if (!isHomeStack() && r.frontOfTask && task.mOnTopOfHome) {
+                    } else if (!isHomeStack() && r.frontOfTask && task.isOverHomeStack()) {
                         if (DEBUG_VISBILITY) Slog.v(TAG, "Showing home: at " + r);
                         behindFullscreen = true;
                     }
@@ -1422,6 +1393,7 @@
         final boolean userLeaving = mStackSupervisor.mUserLeaving;
         mStackSupervisor.mUserLeaving = false;
 
+        final TaskRecord prevTask = prev != null ? prev.task : null;
         if (next == null) {
             // There are no more activities!  Let's just start up the
             // Launcher...
@@ -1429,7 +1401,10 @@
             if (DEBUG_STATES) Slog.d(TAG, "resumeTopActivityLocked: No more activities go home");
             if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
             // Only resume home if on home display
-            return isOnHomeDisplay() && mStackSupervisor.resumeHomeActivity(prev);
+            final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
+                    HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
+            return isOnHomeDisplay() &&
+                    mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
         }
 
         next.delayedResume = false;
@@ -1448,22 +1423,24 @@
         }
 
         final TaskRecord nextTask = next.task;
-        final TaskRecord prevTask = prev != null ? prev.task : null;
         if (prevTask != null && prevTask.stack == this &&
-                prevTask.mOnTopOfHome && prev.finishing && prev.frontOfTask) {
+                prevTask.isOverHomeStack() && prev.finishing && prev.frontOfTask) {
             if (DEBUG_STACK)  mStackSupervisor.validateTopActivitiesLocked();
             if (prevTask == nextTask) {
                 prevTask.setFrontOfTask();
             } else if (prevTask != topTask()) {
-                // This task is going away but it was supposed to return to the home task.
+                // This task is going away but it was supposed to return to the home stack.
                 // Now the task above it has to return to the home task instead.
                 final int taskNdx = mTaskHistory.indexOf(prevTask) + 1;
-                mTaskHistory.get(taskNdx).mOnTopOfHome = true;
+                mTaskHistory.get(taskNdx).setTaskToReturnTo(HOME_ACTIVITY_TYPE);
             } else {
                 if (DEBUG_STATES && isOnHomeDisplay()) Slog.d(TAG,
                         "resumeTopActivityLocked: Launching home next");
                 // Only resume home if on home display
-                return isOnHomeDisplay() && mStackSupervisor.resumeHomeActivity(prev);
+                final int returnTaskType = prevTask == null || !prevTask.isOverHomeStack() ?
+                        HOME_ACTIVITY_TYPE : prevTask.getTaskToReturnTo();
+                return isOnHomeDisplay() &&
+                        mStackSupervisor.resumeHomeStackTask(returnTaskType, prev);
             }
         }
 
@@ -1834,10 +1811,11 @@
             ActivityStack lastStack = mStackSupervisor.getLastStack();
             final boolean fromHome = lastStack.isHomeStack();
             if (!isHomeStack() && (fromHome || topTask() != task)) {
-                task.mOnTopOfHome = fromHome;
+                task.setTaskToReturnTo(fromHome ?
+                        lastStack.topTask().taskType : APPLICATION_ACTIVITY_TYPE);
             }
         } else {
-            task.mOnTopOfHome = false;
+            task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
         }
 
         mTaskHistory.remove(task);
@@ -1954,8 +1932,7 @@
                 // existing activities from other tasks in to it.
                 // If the caller has requested that the target task be
                 // reset, then do so.
-                if ((r.intent.getFlags()
-                        & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
+                if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED) != 0) {
                     resetTaskIfNeededLocked(r, r);
                     doShow = topRunningNonDelayedActivityLocked(null) == r;
                 }
@@ -2048,7 +2025,8 @@
         // the root, we may no longer have the task!).
         final ArrayList<ActivityRecord> activities = task.mActivities;
         final int numActivities = activities.size();
-        for (int i = numActivities - 1; i > 0; --i ) {
+        final int rootActivityNdx = task.findEffectiveRootIndex();
+        for (int i = numActivities - 1; i > rootActivityNdx; --i ) {
             ActivityRecord target = activities.get(i);
 
             final int flags = target.info.flags;
@@ -2207,8 +2185,10 @@
 
         final ArrayList<ActivityRecord> activities = affinityTask.mActivities;
         final int numActivities = activities.size();
-        // Do not operate on the root Activity.
-        for (int i = numActivities - 1; i > 0; --i) {
+        final int rootActivityNdx = affinityTask.findEffectiveRootIndex();
+
+        // Do not operate on or below the effective root Activity.
+        for (int i = numActivities - 1; i > rootActivityNdx; --i) {
             ActivityRecord target = activities.get(i);
 
             final int flags = target.info.flags;
@@ -2382,8 +2362,8 @@
             ActivityRecord next = topRunningActivityLocked(null);
             if (next != r) {
                 final TaskRecord task = r.task;
-                if (r.frontOfTask && task == topTask() && task.mOnTopOfHome) {
-                    mStackSupervisor.moveHomeToTop();
+                if (r.frontOfTask && task == topTask() && task.isOverHomeStack()) {
+                    mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
                 }
             }
             ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
@@ -2875,8 +2855,9 @@
         if (task != null && task.removeActivity(r)) {
             if (DEBUG_STACK) Slog.i(TAG,
                     "removeActivityFromHistoryLocked: last activity removed from " + this);
-            if (mStackSupervisor.isFrontStack(this) && task == topTask() && task.mOnTopOfHome) {
-                mStackSupervisor.moveHomeToTop();
+            if (mStackSupervisor.isFrontStack(this) && task == topTask() &&
+                    task.isOverHomeStack()) {
+                mStackSupervisor.moveHomeStackTaskToTop(task.getTaskToReturnTo());
             }
             removeTask(task);
         }
@@ -3192,12 +3173,13 @@
         }
     }
 
-    void moveHomeTaskToTop() {
+    void moveHomeStackTaskToTop(int homeStackTaskType) {
         final int top = mTaskHistory.size() - 1;
         for (int taskNdx = top; taskNdx >= 0; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
-            if (task.isHomeTask()) {
-                if (DEBUG_TASKS || DEBUG_STACK) Slog.d(TAG, "moveHomeTaskToTop: moving " + task);
+            if (task.taskType == homeStackTaskType) {
+                if (DEBUG_TASKS || DEBUG_STACK)
+                    Slog.d(TAG, "moveHomeStackTaskToTop: moving " + task);
                 mTaskHistory.remove(taskNdx);
                 mTaskHistory.add(top, task);
                 updateTaskMovement(task, true);
@@ -3309,12 +3291,12 @@
         int numTasks = mTaskHistory.size();
         for (int taskNdx = numTasks - 1; taskNdx >= 1; --taskNdx) {
             final TaskRecord task = mTaskHistory.get(taskNdx);
-            if (task.mOnTopOfHome) {
+            if (task.isOverHomeStack()) {
                 break;
             }
             if (taskNdx == 1) {
                 // Set the last task before tr to go to home.
-                task.mOnTopOfHome = true;
+                task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
             }
         }
 
@@ -3335,9 +3317,10 @@
         }
 
         final TaskRecord task = mResumedActivity != null ? mResumedActivity.task : null;
-        if (task == tr && tr.mOnTopOfHome || numTasks <= 1 && isOnHomeDisplay()) {
-            tr.mOnTopOfHome = false;
-            return mStackSupervisor.resumeHomeActivity(null);
+        if (task == tr && tr.isOverHomeStack() || numTasks <= 1 && isOnHomeDisplay()) {
+            final int taskToReturnTo = tr.getTaskToReturnTo();
+            tr.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+            return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null);
         }
 
         mStackSupervisor.resumeTopActivitiesLocked();
@@ -3778,8 +3761,11 @@
 
         final int taskNdx = mTaskHistory.indexOf(task);
         final int topTaskNdx = mTaskHistory.size() - 1;
-        if (task.mOnTopOfHome && taskNdx < topTaskNdx) {
-            mTaskHistory.get(taskNdx + 1).mOnTopOfHome = true;
+        if (task.isOverHomeStack() && taskNdx < topTaskNdx) {
+            final TaskRecord nextTask = mTaskHistory.get(taskNdx + 1);
+            if (!nextTask.isOverHomeStack()) {
+                nextTask.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
+            }
         }
         mTaskHistory.remove(task);
         updateTaskMovement(task, true);
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 66e9eb3e..e7f5720 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -31,6 +31,9 @@
 import static com.android.server.am.ActivityManagerService.DEBUG_USER_LEAVING;
 import static com.android.server.am.ActivityManagerService.FIRST_SUPERVISOR_STACK_MSG;
 import static com.android.server.am.ActivityManagerService.TAG;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
 
 import android.app.Activity;
 import android.app.ActivityManager;
@@ -46,6 +49,8 @@
 import android.app.IActivityManager.WaitResult;
 import android.app.ResultInfo;
 import android.app.StatusBarManager;
+import android.app.admin.DevicePolicyManager;
+import android.app.admin.IDevicePolicyManager;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.IIntentSender;
@@ -77,6 +82,8 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.os.UserHandle;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.EventLog;
 import android.util.Slog;
@@ -140,9 +147,12 @@
 
     private final static String VIRTUAL_DISPLAY_BASE_NAME = "ActivityViewVirtualDisplay";
 
+    private static final String LOCK_TASK_TAG = "Lock-to-App";
+
     /** Status Bar Service **/
     private IBinder mToken = new Binder();
     private IStatusBarService mStatusBarService;
+    private IDevicePolicyManager mDevicePolicyManager;
 
     // For debugging to make sure the caller when acquiring/releasing our
     // wake lock is the system process.
@@ -227,14 +237,14 @@
      * receivers to launch an activity and get that to run before the device
      * goes back to sleep.
      */
-    final PowerManager.WakeLock mLaunchingActivity;
+    PowerManager.WakeLock mLaunchingActivity;
 
     /**
      * Set when the system is going to sleep, until we have
      * successfully paused the current activity and released our wake lock.
      * At that point the system is allowed to actually sleep.
      */
-    final PowerManager.WakeLock mGoingToSleep;
+    PowerManager.WakeLock mGoingToSleep;
 
     /** Stack id of the front stack when user switched, indexed by userId. */
     SparseIntArray mUserStackInFront = new SparseIntArray(2);
@@ -251,16 +261,24 @@
 
     /** If non-null then the task specified remains in front and no other tasks may be started
      * until the task exits or #stopLockTaskMode() is called. */
-    private TaskRecord mLockTaskModeTask;
+    TaskRecord mLockTaskModeTask;
+    /**
+     * Notifies the user when entering/exiting lock-task.
+     */
+    private LockTaskNotify mLockTaskNotify;
 
     public ActivityStackSupervisor(ActivityManagerService service) {
         mService = service;
+        mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
+    }
+
+    /**
+     * At the time when the constructor runs, the power manager has not yet been
+     * initialized.  So we initialize our wakelocks afterwards.
+     */
+    void initPowerManagement() {
         PowerManager pm = (PowerManager)mService.mContext.getSystemService(Context.POWER_SERVICE);
         mGoingToSleep = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Sleep");
-        mHandler = new ActivityStackSupervisorHandler(mService.mHandler.getLooper());
-        if (VALIDATE_WAKE_LOCK_CALLER && Binder.getCallingUid() != Process.myUid()) {
-            throw new IllegalStateException("Calling must be system uid");
-        }
         mLaunchingActivity =
                 pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ActivityManager-Launch");
         mLaunchingActivity.setReferenceCounted(false);
@@ -281,6 +299,19 @@
         }
     }
 
+    private IDevicePolicyManager getDevicePolicyManager() {
+        synchronized (mService) {
+            if (mDevicePolicyManager == null) {
+                mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
+                    ServiceManager.checkService(Context.DEVICE_POLICY_SERVICE));
+                if (mDevicePolicyManager == null) {
+                    Slog.w(TAG, "warning: no DEVICE_POLICY_SERVICE");
+                }
+            }
+            return mDevicePolicyManager;
+        }
+    }
+
     void setWindowManager(WindowManagerService wm) {
         synchronized (mService) {
             mWindowManager = wm;
@@ -354,17 +385,27 @@
         }
     }
 
-    void moveHomeToTop() {
+    void moveHomeStackTaskToTop(int homeStackTaskType) {
+        if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
+            mWindowManager.showRecentApps();
+            return;
+        }
         moveHomeStack(true);
-        mHomeStack.moveHomeTaskToTop();
+        mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
     }
 
-    boolean resumeHomeActivity(ActivityRecord prev) {
-        moveHomeToTop();
-        if (prev != null) {
-            prev.task.mOnTopOfHome = false;
+    boolean resumeHomeStackTask(int homeStackTaskType, ActivityRecord prev) {
+        if (homeStackTaskType == RECENTS_ACTIVITY_TYPE) {
+            mWindowManager.showRecentApps();
+            return false;
         }
+        moveHomeStackTaskToTop(homeStackTaskType);
+        if (prev != null) {
+            prev.task.setTaskToReturnTo(APPLICATION_ACTIVITY_TYPE);
+        }
+
         ActivityRecord r = mHomeStack.topRunningActivityLocked(null);
+        // if (r != null && (r.isHomeActivity() || r.isRecentsActivity())) {
         if (r != null && r.isHomeActivity()) {
             mService.setFocusedActivityLocked(r);
             return resumeTopActivitiesLocked(mHomeStack, prev, null);
@@ -719,7 +760,7 @@
     }
 
     void startHomeActivity(Intent intent, ActivityInfo aInfo) {
-        moveHomeToTop();
+        moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE);
         startActivityLocked(null, intent, null, aInfo, null, null, null, null, 0, 0, 0, null, 0,
                 null, false, null, null);
     }
@@ -1158,7 +1199,8 @@
                     // to run in multiple processes, because this is actually
                     // part of the framework so doesn't make sense to track as a
                     // separate apk in the process.
-                    app.addPackage(r.info.packageName, mService.mProcessStats);
+                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
+                            mService.mProcessStats);
                 }
                 realStartActivityLocked(r, app, andResume, checkConfig);
                 return;
@@ -1668,7 +1710,7 @@
                                     (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME))
                                     == (FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_TASK_ON_HOME)) {
                                 // Caller wants to appear on home activity.
-                                intentActivity.task.mOnTopOfHome = true;
+                                intentActivity.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
                             }
                             options = null;
                         }
@@ -1864,7 +1906,7 @@
                         == (Intent.FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_TASK_ON_HOME)) {
                     // Caller wants to appear on home activity, so before starting
                     // their own activity we will bring home to the front.
-                    r.task.mOnTopOfHome = r.task.stack.isOnHomeDisplay();
+                    r.task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
                 }
             }
         } else if (sourceRecord != null) {
@@ -1939,6 +1981,9 @@
         mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,
                 intent, r.getUriPermissionsLocked(), r.userId);
 
+        if (sourceRecord != null && sourceRecord.isRecentsActivity()) {
+            r.task.setTaskToReturnTo(RECENTS_ACTIVITY_TYPE);
+        }
         if (newTask) {
             EventLog.writeEvent(EventLogTags.AM_CREATE_TASK, r.userId, r.task.taskId);
         }
@@ -2215,7 +2260,7 @@
         if ((flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0) {
             // Caller wants the home activity moved with it.  To accomplish this,
             // we'll just indicate that this task returns to the home task.
-            task.mOnTopOfHome = true;
+            task.setTaskToReturnTo(HOME_ACTIVITY_TYPE);
         }
         task.stack.moveTaskToFrontLocked(task, null, options);
         if (DEBUG_STACK) Slog.d(TAG, "findTaskToMoveToFront: moved to front of stack="
@@ -2327,7 +2372,7 @@
             }
             mWindowManager.addTask(taskId, stackId, false);
         }
-        resumeHomeActivity(null);
+        resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
     }
 
     void moveTaskToStack(int taskId, int stackId, boolean toTop) {
@@ -2589,7 +2634,7 @@
             }
         } else {
             // Stack was moved to another display while user was swapped out.
-            resumeHomeActivity(null);
+            resumeHomeStackTask(HOME_ACTIVITY_TYPE, null);
         }
         return homeInFront;
     }
@@ -2984,13 +3029,16 @@
         return list;
     }
 
-    void setLockTaskModeLocked(TaskRecord task) {
-        final Message lockTaskMsg = Message.obtain();
+    void setLockTaskModeLocked(TaskRecord task, boolean showHomeRecents) {
         if (task == null) {
-            // Take out of lock task mode.
-            mLockTaskModeTask = null;
-            lockTaskMsg.what = LOCK_TASK_END_MSG;
-            mHandler.sendMessage(lockTaskMsg);
+            // Take out of lock task mode if necessary
+            if (mLockTaskModeTask != null) {
+                final Message lockTaskMsg = Message.obtain();
+                lockTaskMsg.arg1 = mLockTaskModeTask.userId;
+                lockTaskMsg.what = LOCK_TASK_END_MSG;
+                mLockTaskModeTask = null;
+                mHandler.sendMessage(lockTaskMsg);
+            }
             return;
         }
         if (isLockTaskModeViolation(task)) {
@@ -3000,7 +3048,12 @@
         mLockTaskModeTask = task;
         findTaskToMoveToFrontLocked(task, 0, null);
         resumeTopActivitiesLocked();
+
+        final Message lockTaskMsg = Message.obtain();
+        lockTaskMsg.obj = mLockTaskModeTask.intent.getComponent().getPackageName();
+        lockTaskMsg.arg1 = mLockTaskModeTask.userId;
         lockTaskMsg.what = LOCK_TASK_START_MSG;
+        lockTaskMsg.arg2 = showHomeRecents ? 1 : 0;
         mHandler.sendMessage(lockTaskMsg);
     }
 
@@ -3103,10 +3156,24 @@
                 case LOCK_TASK_START_MSG: {
                     // When lock task starts, we disable the status bars.
                     try {
+                        if (mLockTaskNotify == null) {
+                            mLockTaskNotify = new LockTaskNotify(mService.mContext);
+                        }
+                        mLockTaskNotify.show(true);
                         if (getStatusBarService() != null) {
-                            getStatusBarService().disable
-                                (StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK,
-                                mToken, mService.mContext.getPackageName());
+                            int flags =
+                                    StatusBarManager.DISABLE_MASK ^ StatusBarManager.DISABLE_BACK;
+                            if (msg.arg2 != 0) {
+                                flags ^= StatusBarManager.DISABLE_HOME
+                                        | StatusBarManager.DISABLE_RECENT;
+                            }
+                            getStatusBarService().disable(flags, mToken,
+                                    mService.mContext.getPackageName());
+                        }
+                        mWindowManager.disableKeyguard(mToken, LOCK_TASK_TAG);
+                        if (getDevicePolicyManager() != null) {
+                            getDevicePolicyManager().notifyLockTaskModeChanged(true,
+                                    (String)msg.obj, msg.arg1);
                         }
                     } catch (RemoteException ex) {
                         throw new RuntimeException(ex);
@@ -3115,11 +3182,29 @@
                 case LOCK_TASK_END_MSG: {
                     // When lock task ends, we enable the status bars.
                     try {
-                       if (getStatusBarService() != null) {
-                           getStatusBarService().disable
-                               (StatusBarManager.DISABLE_NONE,
-                               mToken, mService.mContext.getPackageName());
-                       }
+                        if (getStatusBarService() != null) {
+                            getStatusBarService().disable(StatusBarManager.DISABLE_NONE, mToken,
+                                    mService.mContext.getPackageName());
+                        }
+                        mWindowManager.reenableKeyguard(mToken);
+                        if (getDevicePolicyManager() != null) {
+                            getDevicePolicyManager().notifyLockTaskModeChanged(false, null,
+                                    msg.arg1);
+                        }
+                        if (mLockTaskNotify == null) {
+                            mLockTaskNotify = new LockTaskNotify(mService.mContext);
+                        }
+                        mLockTaskNotify.show(false);
+                        try {
+                            boolean shouldLockKeyguard = Settings.System.getInt(
+                                    mService.mContext.getContentResolver(),
+                                    Settings.System.LOCK_TO_APP_EXIT_LOCKED) != 0;
+                            if (shouldLockKeyguard) {
+                                mWindowManager.lockNow(null);
+                            }
+                        } catch (SettingNotFoundException e) {
+                            // No setting, don't lock.
+                        }
                     } catch (RemoteException ex) {
                         throw new RuntimeException(ex);
                     }
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index eb253eb..3cbc6e2 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -74,12 +74,19 @@
         mStats.setRadioScanningTimeout(mContext.getResources().getInteger(
                 com.android.internal.R.integer.config_radioScanningTimeout)
                 * 1000L);
+    }
+
+    /**
+     * At the time when the constructor runs, the power manager has not yet been
+     * initialized.  So we initialize the low power observer later.
+     */
+    public void initPowerManagement() {
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
         mPowerManagerInternal.registerLowPowerModeObserver(this);
         mStats.noteLowPowerMode(mPowerManagerInternal.getLowPowerModeEnabled());
         (new WakeupReasonThread()).start();
-     }
-    
+    }
+
     public void shutdown() {
         Slog.w("BatteryStats", "Writing battery stats before shutdown...");
         synchronized (mStats) {
@@ -386,6 +393,20 @@
         }
     }
 
+    public void noteWifiSupplicantStateChanged(int supplState, boolean failedAuth) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteWifiSupplicantStateChangedLocked(supplState, failedAuth);
+        }
+    }
+
+    public void noteWifiRssiChanged(int newRssi) {
+        enforceCallingPermission();
+        synchronized (mStats) {
+            mStats.noteWifiRssiChangedLocked(newRssi);
+        }
+    }
+
     public void noteBluetoothOn() {
         enforceCallingPermission();
         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 7b2b04d..cdcc74b 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -887,7 +887,8 @@
                     info.activityInfo.applicationInfo.uid, false);
             if (app != null && app.thread != null) {
                 try {
-                    app.addPackage(info.activityInfo.packageName, mService.mProcessStats);
+                    app.addPackage(info.activityInfo.packageName,
+                            info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
                     processCurBroadcastLocked(r, app);
                     return;
                 } catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/am/LockTaskNotify.java b/services/core/java/com/android/server/am/LockTaskNotify.java
new file mode 100644
index 0000000..1997f46
--- /dev/null
+++ b/services/core/java/com/android/server/am/LockTaskNotify.java
@@ -0,0 +1,185 @@
+/*
+ * 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.am;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.graphics.PixelFormat;
+import android.os.Handler;
+import android.os.Message;
+import android.util.DisplayMetrics;
+import android.util.Slog;
+import android.view.Gravity;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import com.android.internal.R;
+
+/**
+ *  Helper to manage showing/hiding a image to notify them that they are entering
+ *  or exiting lock-to-app mode.
+ */
+public class LockTaskNotify {
+    private static final String TAG = "LockTaskNotify";
+
+    private static final int SHOW_LENGTH_MS = 1500;
+
+    private final Context mContext;
+    private final H mHandler;
+
+    private ClingWindowView mClingWindow;
+    private WindowManager mWindowManager;
+    private boolean mIsStarting;
+
+    public LockTaskNotify(Context context) {
+        mContext = context;
+        mHandler = new H();
+        mWindowManager = (WindowManager)
+                mContext.getSystemService(Context.WINDOW_SERVICE);
+    }
+
+    public void show(boolean starting) {
+        mIsStarting = starting;
+        mHandler.obtainMessage(H.SHOW).sendToTarget();
+    }
+
+    public WindowManager.LayoutParams getClingWindowLayoutParams() {
+        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                ViewGroup.LayoutParams.MATCH_PARENT,
+                WindowManager.LayoutParams.TYPE_TOAST,
+                0
+                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                        | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED
+                ,
+                PixelFormat.TRANSLUCENT);
+        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+        lp.setTitle("LockTaskNotify");
+        lp.windowAnimations = com.android.internal.R.style.Animation_RecentApplications;
+        lp.gravity = Gravity.FILL;
+        return lp;
+    }
+
+    public FrameLayout.LayoutParams getImageLayoutParams() {
+        return new FrameLayout.LayoutParams(
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.WRAP_CONTENT,
+                Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL);
+    }
+
+    private void handleShow() {
+        mClingWindow = new ClingWindowView(mContext);
+
+        // we will be hiding the nav bar, so layout as if it's already hidden
+        mClingWindow.setSystemUiVisibility(
+                View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+              | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
+
+        // show the confirmation
+        WindowManager.LayoutParams lp = getClingWindowLayoutParams();
+        mWindowManager.addView(mClingWindow, lp);
+    }
+
+    private void handleHide() {
+        if (mClingWindow != null) {
+            mWindowManager.removeView(mClingWindow);
+            mClingWindow = null;
+        }
+    }
+
+
+    private class ClingWindowView extends FrameLayout {
+        private View mView;
+
+        private Runnable mUpdateLayoutRunnable = new Runnable() {
+            @Override
+            public void run() {
+                if (mView != null && mView.getParent() != null) {
+                    mView.setLayoutParams(getImageLayoutParams());
+                }
+            }
+        };
+
+        private BroadcastReceiver mReceiver = new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                if (intent.getAction().equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
+                    post(mUpdateLayoutRunnable);
+                }
+            }
+        };
+
+        public ClingWindowView(Context context) {
+            super(context);
+            setClickable(true);
+        }
+
+        @Override
+        public void onAttachedToWindow() {
+            super.onAttachedToWindow();
+
+            DisplayMetrics metrics = new DisplayMetrics();
+            mWindowManager.getDefaultDisplay().getMetrics(metrics);
+
+            int id = R.layout.lock_to_app_exit;
+            if (mIsStarting) {
+                id = R.layout.lock_to_app_enter;
+            }
+            mView = View.inflate(getContext(), id, null);
+
+            addView(mView, getImageLayoutParams());
+
+            mContext.registerReceiver(mReceiver,
+                    new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED));
+            mHandler.sendMessageDelayed(mHandler.obtainMessage(H.HIDE), SHOW_LENGTH_MS);
+        }
+
+        @Override
+        public void onDetachedFromWindow() {
+            mContext.unregisterReceiver(mReceiver);
+        }
+
+        @Override
+        public boolean onTouchEvent(MotionEvent motion) {
+            Slog.v(TAG, "ClingWindowView.onTouchEvent");
+            return true;
+        }
+    }
+
+    private final class H extends Handler {
+        private static final int SHOW = 1;
+        private static final int HIDE = 2;
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch(msg.what) {
+                case SHOW:
+                    handleShow();
+                    break;
+                case HIDE:
+                    handleHide();
+                    break;
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/am/LockToAppRequestDialog.java b/services/core/java/com/android/server/am/LockToAppRequestDialog.java
new file mode 100644
index 0000000..6e86dff
--- /dev/null
+++ b/services/core/java/com/android/server/am/LockToAppRequestDialog.java
@@ -0,0 +1,87 @@
+
+package com.android.server.am;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.res.Resources;
+import android.graphics.BitmapFactory;
+import android.provider.Settings;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.style.DynamicDrawableSpan;
+import android.text.style.ImageSpan;
+import android.util.Slog;
+import android.view.WindowManager;
+import android.widget.TextView;
+
+import com.android.internal.R;
+
+public class LockToAppRequestDialog implements OnClickListener {
+    private static final String TAG = "ActivityManager";
+
+    final private Context mContext;
+    final private ActivityManagerService mService;
+
+    private AlertDialog mDialog;
+    private TaskRecord mRequestedTask;
+
+    public LockToAppRequestDialog(Context context, ActivityManagerService activityManagerService) {
+        mContext = context;
+        mService = activityManagerService;
+    }
+
+    public void showLockTaskPrompt(TaskRecord task) {
+        if (mDialog != null) {
+            mDialog.dismiss();
+            mDialog = null;
+        }
+        mRequestedTask = task;
+
+        final Resources r = Resources.getSystem();
+        final String descriptionString = r.getString(R.string.lock_to_app_description);
+        final SpannableString description =
+                new SpannableString(descriptionString.replace('$', ' '));
+        final ImageSpan imageSpan = new ImageSpan(mContext,
+                BitmapFactory.decodeResource(r, R.drawable.ic_recent),
+                DynamicDrawableSpan.ALIGN_BOTTOM);
+        final int index = descriptionString.indexOf('$');
+        if (index >= 0) {
+            description.setSpan(imageSpan, index, index + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+        }
+        mDialog =
+                new AlertDialog.Builder(mContext)
+                        .setTitle(r.getString(R.string.lock_to_app_title))
+                        .setMessage(description)
+                        .setPositiveButton(r.getString(R.string.lock_to_app_positive), this)
+                        .setNegativeButton(r.getString(R.string.lock_to_app_negative), this)
+                        .create();
+
+        mDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
+        mDialog.show();
+
+        // Make icon fit.
+        final TextView msgTxt = (TextView) mDialog.findViewById(R.id.message);
+        final float width = imageSpan.getDrawable().getIntrinsicWidth();
+        final float height = imageSpan.getDrawable().getIntrinsicHeight();
+        final int lineHeight = msgTxt.getLineHeight();
+        imageSpan.getDrawable().setBounds(0, 0, (int) (lineHeight * width / height), lineHeight);
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        if (DialogInterface.BUTTON_POSITIVE == which) {
+            Slog.d(TAG, "accept lock-to-app request");
+            // Automatically enable if not currently on. (Could be triggered by an app)
+            Settings.System.putInt(mContext.getContentResolver(),
+                    Settings.System.LOCK_TO_APP_ENABLED, 1);
+
+            // Start lock-to-app.
+            mService.startLockTaskMode(mRequestedTask);
+        } else {
+            Slog.d(TAG, "ignore lock-to-app request");
+        }
+    }
+
+}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 8d7d300..2f25bd4 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -53,8 +53,8 @@
     final int userId;           // user of process.
     final String processName;   // name of the process
     // List of packages running in the process
-    final ArrayMap<String, ProcessStats.ProcessState> pkgList
-            = new ArrayMap<String, ProcessStats.ProcessState>();
+    final ArrayMap<String, ProcessStats.ProcessStateHolder> pkgList
+            = new ArrayMap<String, ProcessStats.ProcessStateHolder>();
     IApplicationThread thread;  // the actual proc...  may be null only if
                                 // 'persistent' is true (in which case we
                                 // are in the process of launching the app)
@@ -371,7 +371,7 @@
         uid = _uid;
         userId = UserHandle.getUserId(_uid);
         processName = _processName;
-        pkgList.put(_info.packageName, null);
+        pkgList.put(_info.packageName, new ProcessStats.ProcessStateHolder(_info.versionCode));
         maxAdj = ProcessList.UNKNOWN_ADJ;
         curRawAdj = setRawAdj = -100;
         curAdj = setAdj = -100;
@@ -398,16 +398,15 @@
                     info.versionCode, processName);
             baseProcessTracker.makeActive();
             for (int i=0; i<pkgList.size(); i++) {
-                ProcessStats.ProcessState ps = pkgList.valueAt(i);
-                if (ps != null && ps != origBase) {
-                    ps.makeInactive();
+                ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+                if (holder.state != null && holder.state != origBase) {
+                    holder.state.makeInactive();
                 }
-                ps = tracker.getProcessStateLocked(pkgList.keyAt(i), info.uid,
+                holder.state = tracker.getProcessStateLocked(pkgList.keyAt(i), info.uid,
                         info.versionCode, processName);
-                if (ps != baseProcessTracker) {
-                    ps.makeActive();
+                if (holder.state != baseProcessTracker) {
+                    holder.state.makeActive();
                 }
-                pkgList.setValueAt(i, ps);
             }
         }
         thread = _thread;
@@ -424,11 +423,11 @@
             }
             baseProcessTracker = null;
             for (int i=0; i<pkgList.size(); i++) {
-                ProcessStats.ProcessState ps = pkgList.valueAt(i);
-                if (ps != null && ps != origBase) {
-                    ps.makeInactive();
+                ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+                if (holder.state != null && holder.state != origBase) {
+                    holder.state.makeInactive();
                 }
-                pkgList.setValueAt(i, null);
+                holder.state = null;
             }
         }
     }
@@ -572,17 +571,19 @@
     /*
      *  Return true if package has been added false if not
      */
-    public boolean addPackage(String pkg, ProcessStatsService tracker) {
+    public boolean addPackage(String pkg, int versionCode, ProcessStatsService tracker) {
         if (!pkgList.containsKey(pkg)) {
+            ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+                    versionCode);
             if (baseProcessTracker != null) {
-                ProcessStats.ProcessState state = tracker.getProcessStateLocked(
-                        pkg, info.uid, info.versionCode, processName);
-                pkgList.put(pkg, state);
-                if (state != baseProcessTracker) {
-                    state.makeActive();
+                holder.state = tracker.getProcessStateLocked(
+                        pkg, info.uid, versionCode, processName);
+                pkgList.put(pkg, holder);
+                if (holder.state != baseProcessTracker) {
+                    holder.state.makeActive();
                 }
             } else {
-                pkgList.put(pkg, null);
+                pkgList.put(pkg, holder);
             }
             return true;
         }
@@ -615,23 +616,26 @@
                     tracker.getMemFactorLocked(), now, pkgList);
             if (N != 1) {
                 for (int i=0; i<N; i++) {
-                    ProcessStats.ProcessState ps = pkgList.valueAt(i);
-                    if (ps != null && ps != baseProcessTracker) {
-                        ps.makeInactive();
+                    ProcessStats.ProcessStateHolder holder = pkgList.valueAt(i);
+                    if (holder.state != null && holder.state != baseProcessTracker) {
+                        holder.state.makeInactive();
                     }
 
                 }
                 pkgList.clear();
                 ProcessStats.ProcessState ps = tracker.getProcessStateLocked(
                         info.packageName, info.uid, info.versionCode, processName);
-                pkgList.put(info.packageName, ps);
+                ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder(
+                        info.versionCode);
+                holder.state = ps;
+                pkgList.put(info.packageName, holder);
                 if (ps != baseProcessTracker) {
                     ps.makeActive();
                 }
             }
         } else if (N != 1) {
             pkgList.clear();
-            pkgList.put(info.packageName, null);
+            pkgList.put(info.packageName, new ProcessStats.ProcessStateHolder(info.versionCode));
         }
     }
     
diff --git a/services/core/java/com/android/server/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java
index 14f3ef9..7ec14c2 100644
--- a/services/core/java/com/android/server/am/ProcessStatsService.java
+++ b/services/core/java/com/android/server/am/ProcessStatsService.java
@@ -171,10 +171,17 @@
         return mProcessStats.mMemFactor != ProcessStats.STATE_NOTHING ? mProcessStats.mMemFactor : 0;
     }
 
+    public void addSysMemUsageLocked(long cachedMem, long freeMem, long zramMem, long kernelMem,
+            long nativeMem) {
+        mProcessStats.addSysMemUsage(cachedMem, freeMem, zramMem, kernelMem, nativeMem);
+    }
+
     public boolean shouldWriteNowLocked(long now) {
         if (now > (mLastWriteTime+WRITE_PERIOD)) {
             if (SystemClock.elapsedRealtime()
-                    > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD)) {
+                    > (mProcessStats.mTimePeriodStartRealtime+ProcessStats.COMMIT_PERIOD) &&
+                    SystemClock.uptimeMillis()
+                    > (mProcessStats.mTimePeriodStartUptime+ProcessStats.COMMIT_UPTIME_PERIOD)) {
                 mCommitPending = true;
             }
             return true;
@@ -212,6 +219,7 @@
             if (mPendingWrite == null || !mPendingWriteCommitted) {
                 mPendingWrite = Parcel.obtain();
                 mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
+                mProcessStats.mTimePeriodEndUptime = now;
                 if (commit) {
                     mProcessStats.mFlags |= ProcessStats.FLAG_COMPLETE;
                 }
@@ -439,8 +447,10 @@
         mWriteLock.lock();
         try {
             synchronized (mAm) {
+                long now = SystemClock.uptimeMillis();
                 mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
-                mProcessStats.writeToParcel(current, 0);
+                mProcessStats.mTimePeriodEndUptime = now;
+                mProcessStats.writeToParcel(current, now, 0);
             }
             if (historic != null) {
                 ArrayList<String> files = getCommittedFiles(0, false, true);
@@ -470,8 +480,10 @@
             Parcel current = Parcel.obtain();
             long curTime;
             synchronized (mAm) {
+                long now = SystemClock.uptimeMillis();
                 mProcessStats.mTimePeriodEndRealtime = SystemClock.elapsedRealtime();
-                mProcessStats.writeToParcel(current, 0);
+                mProcessStats.mTimePeriodEndUptime = now;
+                mProcessStats.writeToParcel(current, now, 0);
                 curTime = mProcessStats.mTimePeriodEndRealtime
                         - mProcessStats.mTimePeriodStartRealtime;
             }
@@ -568,8 +580,8 @@
     static private void dumpHelp(PrintWriter pw) {
         pw.println("Process stats (procstats) dump options:");
         pw.println("    [--checkin|-c|--csv] [--csv-screen] [--csv-proc] [--csv-mem]");
-        pw.println("    [--details] [--full-details] [--current] [--hours] [--active]");
-        pw.println("    [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]");
+        pw.println("    [--details] [--full-details] [--current] [--hours N] [--last N]");
+        pw.println("    [--active] [--commit] [--reset] [--clear] [--write] [-h] [<package.name>]");
         pw.println("  --checkin: perform a checkin: print and delete old committed states.");
         pw.println("  --c: print only state in checkin format.");
         pw.println("  --csv: output data suitable for putting in a spreadsheet.");
@@ -581,6 +593,7 @@
         pw.println("  --full-details: dump all timing and active state details.");
         pw.println("  --current: only dump current state.");
         pw.println("  --hours: aggregate over about N last hours.");
+        pw.println("  --last: only show the last committed stats at index N (starting at 1).");
         pw.println("  --active: only show currently active processes/services.");
         pw.println("  --commit: commit current stats to disk and reset to start new stats.");
         pw.println("  --reset: reset current stats, without committing.");
@@ -621,6 +634,7 @@
         boolean dumpFullDetails = false;
         boolean dumpAll = false;
         int aggregateHours = 0;
+        int lastIndex = 0;
         boolean activeOnly = false;
         String reqPackage = null;
         boolean csvSepScreenStats = false;
@@ -705,6 +719,20 @@
                         dumpHelp(pw);
                         return;
                     }
+                } else if ("--last".equals(arg)) {
+                    i++;
+                    if (i >= args.length) {
+                        pw.println("Error: argument required for --last");
+                        dumpHelp(pw);
+                        return;
+                    }
+                    try {
+                        lastIndex = Integer.parseInt(args[i]);
+                    } catch (NumberFormatException e) {
+                        pw.println("Error: --last argument not an int -- " + args[i]);
+                        dumpHelp(pw);
+                        return;
+                    }
                 } else if ("--active".equals(arg)) {
                     activeOnly = true;
                     currentOnly = true;
@@ -818,6 +846,43 @@
             dumpAggregatedStats(pw, aggregateHours, now, reqPackage, isCompact,
                     dumpDetails, dumpFullDetails, dumpAll, activeOnly);
             return;
+        } else if (lastIndex > 0) {
+            pw.print("LAST STATS AT INDEX "); pw.print(lastIndex); pw.println(":");
+            ArrayList<String> files = getCommittedFiles(0, false, true);
+            if (lastIndex >= files.size()) {
+                pw.print("Only have "); pw.print(files.size()); pw.println(" data sets");
+                return;
+            }
+            AtomicFile file = new AtomicFile(new File(files.get(lastIndex)));
+            ProcessStats processStats = new ProcessStats(false);
+            readLocked(processStats, file);
+            if (processStats.mReadError != null) {
+                if (isCheckin || isCompact) pw.print("err,");
+                pw.print("Failure reading "); pw.print(files.get(lastIndex));
+                pw.print("; "); pw.println(processStats.mReadError);
+                return;
+            }
+            String fileStr = file.getBaseFile().getPath();
+            boolean checkedIn = fileStr.endsWith(STATE_FILE_CHECKIN_SUFFIX);
+            if (isCheckin || isCompact) {
+                // Don't really need to lock because we uniquely own this object.
+                processStats.dumpCheckinLocked(pw, reqPackage);
+            } else {
+                pw.print("COMMITTED STATS FROM ");
+                pw.print(processStats.mTimePeriodStartClockStr);
+                if (checkedIn) pw.print(" (checked in)");
+                pw.println(":");
+                if (dumpDetails || dumpFullDetails) {
+                    processStats.dumpLocked(pw, reqPackage, now, !dumpFullDetails, dumpAll,
+                            activeOnly);
+                    if (dumpAll) {
+                        pw.print("  mFile="); pw.println(mFile.getBaseFile());
+                    }
+                } else {
+                    processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
+                }
+            }
+            return;
         }
 
         boolean sepNeeded = false;
@@ -859,7 +924,7 @@
                                 // Always dump summary here, dumping all details is just too
                                 // much crud.
                                 if (dumpFullDetails) {
-                                    mProcessStats.dumpLocked(pw, reqPackage, now, false, false,
+                                    processStats.dumpLocked(pw, reqPackage, now, false, false,
                                             activeOnly);
                                 } else {
                                     processStats.dumpSummaryLocked(pw, reqPackage, now, activeOnly);
diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index e54c95e..f79c026 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -45,6 +45,7 @@
 import java.io.PrintWriter;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Objects;
 
 /**
  * A running application service.
@@ -70,9 +71,6 @@
     final String packageName; // the package implementing intent's component
     final String processName; // process where this component wants to run
     final String permission;// permission needed to access service
-    final String baseDir;   // where activity source (resources etc) located
-    final String resDir;   // where public activity source (public resources etc) located
-    final String dataDir;   // where activity data should go
     final boolean exported; // from ServiceInfo.exported
     final Runnable restarter; // used to schedule retries of starting the service
     final long createTime;  // when this service was created
@@ -209,11 +207,13 @@
         }
         long now = SystemClock.uptimeMillis();
         long nowReal = SystemClock.elapsedRealtime();
-        pw.print(prefix); pw.print("baseDir="); pw.println(baseDir);
-        if (!resDir.equals(baseDir)) {
-            pw.print(prefix); pw.print("resDir="); pw.println(resDir);
+        if (appInfo != null) {
+            pw.print(prefix); pw.print("baseDir="); pw.println(appInfo.sourceDir);
+            if (!Objects.equals(appInfo.sourceDir, appInfo.publicSourceDir)) {
+                pw.print(prefix); pw.print("resDir="); pw.println(appInfo.publicSourceDir);
+            }
+            pw.print(prefix); pw.print("dataDir="); pw.println(appInfo.dataDir);
         }
-        pw.print(prefix); pw.print("dataDir="); pw.println(dataDir);
         pw.print(prefix); pw.print("app="); pw.println(app);
         if (isolatedProc != null) {
             pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc);
@@ -305,9 +305,6 @@
         packageName = sInfo.applicationInfo.packageName;
         processName = sInfo.processName;
         permission = sInfo.permission;
-        baseDir = sInfo.applicationInfo.sourceDir;
-        resDir = sInfo.applicationInfo.publicSourceDir;
-        dataDir = sInfo.applicationInfo.dataDir;
         exported = sInfo.exported;
         this.restarter = restarter;
         createTime = SystemClock.elapsedRealtime();
diff --git a/services/core/java/com/android/server/am/TaskPersister.java b/services/core/java/com/android/server/am/TaskPersister.java
index bb289fa..c79b33d 100644
--- a/services/core/java/com/android/server/am/TaskPersister.java
+++ b/services/core/java/com/android/server/am/TaskPersister.java
@@ -45,7 +45,7 @@
     static final boolean DEBUG = false;
 
     /** When in slow mode don't write tasks out faster than this */
-    private static final long INTER_TASK_DELAY_MS = 60000;
+    private static final long INTER_TASK_DELAY_MS = 10000;
     private static final long DEBUG_INTER_TASK_DELAY_MS = 5000;
 
     private static final String RECENTS_FILENAME = "_task";
@@ -69,6 +69,7 @@
     TaskPersister(File systemDir, ActivityStackSupervisor stackSupervisor) {
         sTasksDir = new File(systemDir, TASKS_DIRNAME);
         if (!sTasksDir.exists()) {
+            if (DEBUG) Slog.d(TAG, "Creating tasks directory " + sTasksDir);
             if (!sTasksDir.mkdir()) {
                 Slog.e(TAG, "Failure creating tasks directory " + sTasksDir);
             }
@@ -76,6 +77,7 @@
 
         sImagesDir = new File(systemDir, IMAGES_DIRNAME);
         if (!sImagesDir.exists()) {
+            if (DEBUG) Slog.d(TAG, "Creating images directory " + sTasksDir);
             if (!sImagesDir.mkdir()) {
                 Slog.e(TAG, "Failure creating images directory " + sImagesDir);
             }
@@ -172,14 +174,15 @@
                                     TaskRecord.restoreFromXml(in, mStackSupervisor);
                             if (DEBUG) Slog.d(TAG, "restoreTasksLocked: restored task=" + task);
                             if (task != null) {
+                                task.isPersistable = true;
                                 tasks.add(task);
                                 final int taskId = task.taskId;
                                 recoveredTaskIds.add(taskId);
                                 mStackSupervisor.setNextTaskId(taskId);
                             }
                         } else {
-                            Slog.e(TAG, "restoreTasksLocked Unknown xml event=" + event + " name="
-                                    + name);
+                            Slog.wtf(TAG, "restoreTasksLocked Unknown xml event=" + event +
+                                    " name=" + name);
                         }
                     }
                     XmlUtils.skipCurrentTag(in);
@@ -195,6 +198,7 @@
                     }
                 }
                 if (!DEBUG && deleteFile) {
+                    if (DEBUG) Slog.d(TAG, "Deleting file=" + taskFile.getName());
                     taskFile.delete();
                 }
             }
@@ -209,7 +213,7 @@
         Arrays.sort(tasksArray, new Comparator<TaskRecord>() {
             @Override
             public int compare(TaskRecord lhs, TaskRecord rhs) {
-                final long diff = lhs.mLastTimeMoved - rhs.mLastTimeMoved;
+                final long diff = rhs.mLastTimeMoved - lhs.mLastTimeMoved;
                 if (diff < 0) {
                     return -1;
                 } else if (diff > 0) {
@@ -233,8 +237,7 @@
                 try {
                     taskId = Integer.valueOf(filename.substring(0, taskIdEnd));
                 } catch (Exception e) {
-                    if (DEBUG) Slog.d(TAG, "removeObsoleteFile: Can't parse file=" +
-                            file.getName());
+                    Slog.wtf(TAG, "removeObsoleteFile: Can't parse file=" + file.getName());
                     file.delete();
                     continue;
                 }
@@ -288,15 +291,18 @@
                 synchronized(mService) {
                     final ArrayList<TaskRecord> tasks = mService.mRecentTasks;
                     persistentTaskIds.clear();
+                    if (DEBUG) Slog.d(TAG, "mRecents=" + tasks);
                     for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) {
                         task = tasks.get(taskNdx);
                         if (DEBUG) Slog.d(TAG, "LazyTaskWriter: task=" + task + " persistable=" +
                                 task.isPersistable + " needsPersisting=" + task.needsPersisting);
-                        if (task.isPersistable) {
+                        if (task.isPersistable && !task.stack.isHomeStack()) {
+                            if (DEBUG) Slog.d(TAG, "adding to persistentTaskIds task=" + task);
                             persistentTaskIds.add(task.taskId);
 
                             if (task.needsPersisting) {
                                 try {
+                                    if (DEBUG) Slog.d(TAG, "Saving task=" + task);
                                     stringWriter = saveToXml(task);
                                     break;
                                 } catch (IOException e) {
@@ -305,6 +311,8 @@
                                     task.needsPersisting = false;
                                 }
                             }
+                        } else {
+                            if (DEBUG) Slog.d(TAG, "omitting from persistentTaskIds task=" + task);
                         }
                     }
                 }
@@ -330,6 +338,8 @@
                     // Made it through the entire list and didn't find anything new that needed
                     // persisting.
                     if (!DEBUG) {
+                        if (DEBUG) Slog.d(TAG, "Calling removeObsoleteFiles persistentTaskIds=" +
+                                persistentTaskIds);
                         removeObsoleteFiles(persistentTaskIds);
                     }
 
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 81a0b36..a301c4b 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -17,6 +17,9 @@
 package com.android.server.am;
 
 import static com.android.server.am.ActivityManagerService.TAG;
+import static com.android.server.am.ActivityRecord.HOME_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+import static com.android.server.am.ActivityRecord.RECENTS_ACTIVITY_TYPE;
 import static com.android.server.am.ActivityStackSupervisor.DEBUG_ADD_REMOVE;
 
 import android.app.Activity;
@@ -27,7 +30,6 @@
 import android.content.Intent;
 import android.content.pm.ActivityInfo;
 import android.graphics.Bitmap;
-import android.os.SystemClock;
 import android.os.UserHandle;
 import android.service.voice.IVoiceInteractionSession;
 import android.util.Slog;
@@ -54,14 +56,14 @@
     private static final String ATTR_ASKEDCOMPATMODE = "asked_compat_mode";
     private static final String ATTR_USERID = "user_id";
     private static final String ATTR_TASKTYPE = "task_type";
-    private static final String ATTR_ONTOPOFHOME = "on_top_of_home";
     private static final String ATTR_LASTDESCRIPTION = "last_description";
     private static final String ATTR_LASTTIMEMOVED = "last_time_moved";
+    private static final String ATTR_NEVERRELINQUISH = "never_relinquish_identity";
 
     private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail";
 
     final int taskId;       // Unique identifier for this task.
-    final String affinity;  // The affinity name for this task, or null.
+    String affinity;        // The affinity name for this task, or null.
     final IVoiceInteractionSession voiceSession;    // Voice interaction session driving task
     final IVoiceInteractor voiceInteractor;         // Associated interactor to provide to app
     Intent intent;          // The original intent that started the task.
@@ -107,9 +109,17 @@
     /** True if persistable, has changed, and has not yet been persisted */
     boolean needsPersisting = false;
 
-    /** Launch the home activity when leaving this task. Will be false for tasks that are not on
-     * Display.DEFAULT_DISPLAY. */
-    boolean mOnTopOfHome = false;
+    /** Indication of what to run next when task exits. Use ActivityRecord types.
+     * ActivityRecord.APPLICATION_ACTIVITY_TYPE indicates to resume the task below this one in the
+     * task stack. */
+    private int mTaskToReturnTo = APPLICATION_ACTIVITY_TYPE;
+
+    /** If original intent did not allow relinquishing task identity, save that information */
+    boolean mNeverRelinquishIdentity = true;
+
+    // Used in the unique case where we are clearing the task in order to reuse it. In that case we
+    // do not want to delete the stack when the task goes empty.
+    boolean mReuseTask = false;
 
     final ActivityManagerService mService;
 
@@ -117,7 +127,6 @@
             IVoiceInteractionSession _voiceSession, IVoiceInteractor _voiceInteractor) {
         mService = service;
         taskId = _taskId;
-        affinity = info.taskAffinity;
         voiceSession = _voiceSession;
         voiceInteractor = _voiceInteractor;
         setIntent(_intent, info);
@@ -126,9 +135,9 @@
 
     TaskRecord(ActivityManagerService service, int _taskId, Intent _intent, Intent _affinityIntent,
             String _affinity, ComponentName _realActivity, ComponentName _origActivity,
-            boolean _rootWasReset, boolean _askedCompatMode, int _taskType, boolean _onTopOfHome,
-            int _userId, String _lastDescription, ArrayList<ActivityRecord> activities,
-            long lastTimeMoved) {
+            boolean _rootWasReset, boolean _askedCompatMode, int _taskType, int _userId,
+            String _lastDescription, ArrayList<ActivityRecord> activities, long lastTimeMoved,
+            boolean neverRelinquishIdentity) {
         mService = service;
         taskId = _taskId;
         intent = _intent;
@@ -141,11 +150,12 @@
         rootWasReset = _rootWasReset;
         askedCompatMode = _askedCompatMode;
         taskType = _taskType;
-        mOnTopOfHome = _onTopOfHome;
+        mTaskToReturnTo = HOME_ACTIVITY_TYPE;
         userId = _userId;
         lastDescription = _lastDescription;
         mActivities = activities;
         mLastTimeMoved = lastTimeMoved;
+        mNeverRelinquishIdentity = neverRelinquishIdentity;
     }
 
     void touchActiveTime() {
@@ -157,6 +167,14 @@
     }
 
     void setIntent(Intent _intent, ActivityInfo info) {
+        if (intent == null) {
+            mNeverRelinquishIdentity =
+                    (info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0;
+        } else if (mNeverRelinquishIdentity) {
+            return;
+        }
+
+        affinity = info.taskAffinity;
         stringName = null;
 
         if (info.targetActivity == null) {
@@ -209,6 +227,14 @@
         }
     }
 
+    void setTaskToReturnTo(int taskToReturnTo) {
+        mTaskToReturnTo = taskToReturnTo;
+    }
+
+    int getTaskToReturnTo() {
+        return mTaskToReturnTo;
+    }
+
     void disposeThumbnail() {
         super.disposeThumbnail();
         for (int i=mActivities.size()-1; i>=0; i--) {
@@ -271,6 +297,11 @@
                 foundFront = true;
             }
         }
+        if (!foundFront && numActivities > 0) {
+            // All activities of this task are finishing. As we ought to have a frontOfTask
+            // activity, make the bottom activity front.
+            mActivities.get(0).frontOfTask = true;
+        }
     }
 
     /**
@@ -282,6 +313,7 @@
 
         mActivities.remove(newTop);
         mActivities.add(newTop);
+        updateEffectiveIntent();
 
         setFrontOfTask();
     }
@@ -311,6 +343,7 @@
             r.mActivityType = taskType;
         }
         mActivities.add(index, r);
+        updateEffectiveIntent();
         if (r.isPersistable()) {
             mService.notifyTaskPersisterLocked(this, false);
         }
@@ -325,7 +358,11 @@
         if (r.isPersistable()) {
             mService.notifyTaskPersisterLocked(this, false);
         }
-        return mActivities.size() == 0;
+        if (mActivities.isEmpty()) {
+            return !mReuseTask;
+        }
+        updateEffectiveIntent();
+        return false;
     }
 
     boolean autoRemoveFromRecents() {
@@ -366,7 +403,9 @@
      * Completely remove all activities associated with an existing task.
      */
     final void performClearTaskLocked() {
+        mReuseTask = true;
         performClearTaskAtIndexLocked(0);
+        mReuseTask = false;
     }
 
     /**
@@ -486,11 +525,15 @@
     }
 
     boolean isHomeTask() {
-        return taskType == ActivityRecord.HOME_ACTIVITY_TYPE;
+        return taskType == HOME_ACTIVITY_TYPE;
     }
 
     boolean isApplicationTask() {
-        return taskType == ActivityRecord.APPLICATION_ACTIVITY_TYPE;
+        return taskType == APPLICATION_ACTIVITY_TYPE;
+    }
+
+    boolean isOverHomeStack() {
+        return mTaskToReturnTo == HOME_ACTIVITY_TYPE || mTaskToReturnTo == RECENTS_ACTIVITY_TYPE;
     }
 
     public TaskAccessInfo getTaskAccessInfoLocked() {
@@ -579,12 +622,19 @@
         // utility activities.
         int activityNdx;
         final int numActivities = mActivities.size();
+        final boolean relinquish = numActivities == 0 ? false :
+                (mActivities.get(0).info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) != 0;
         for (activityNdx = Math.min(numActivities, 1); activityNdx < numActivities;
                 ++activityNdx) {
             final ActivityRecord r = mActivities.get(activityNdx);
+            if (relinquish && (r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
+                // This will be the top activity for determining taskDescription. Pre-inc to
+                // overcome initial decrement below.
+                ++activityNdx;
+                break;
+            }
             if (r.intent != null &&
-                    (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
-                            != 0) {
+                    (r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) {
                 break;
             }
         }
@@ -615,6 +665,27 @@
         }
     }
 
+    int findEffectiveRootIndex() {
+        int activityNdx;
+        final int topActivityNdx = mActivities.size() - 1;
+        for (activityNdx = 0; activityNdx < topActivityNdx; ++activityNdx) {
+            final ActivityRecord r = mActivities.get(activityNdx);
+            if (r.finishing) {
+                continue;
+            }
+            if ((r.info.flags & ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY) == 0) {
+                break;
+            }
+        }
+        return activityNdx;
+    }
+
+    void updateEffectiveIntent() {
+        final int effectiveRootIndex = findEffectiveRootIndex();
+        final ActivityRecord r = mActivities.get(effectiveRootIndex);
+        setIntent(r.intent, r.info);
+    }
+
     void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
         Slog.i(TAG, "Saving task=" + this);
 
@@ -632,8 +703,8 @@
         out.attribute(null, ATTR_ASKEDCOMPATMODE, String.valueOf(askedCompatMode));
         out.attribute(null, ATTR_USERID, String.valueOf(userId));
         out.attribute(null, ATTR_TASKTYPE, String.valueOf(taskType));
-        out.attribute(null, ATTR_ONTOPOFHOME, String.valueOf(mOnTopOfHome));
         out.attribute(null, ATTR_LASTTIMEMOVED, String.valueOf(mLastTimeMoved));
+        out.attribute(null, ATTR_NEVERRELINQUISH, String.valueOf(mNeverRelinquishIdentity));
         if (lastDescription != null) {
             out.attribute(null, ATTR_LASTDESCRIPTION, lastDescription.toString());
         }
@@ -652,8 +723,9 @@
         final int numActivities = activities.size();
         for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {
             final ActivityRecord r = activities.get(activityNdx);
-            if (!r.isPersistable() || (activityNdx > 0 &&
-                    (r.intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0)) {
+            if (r.info.persistableMode == ActivityInfo.PERSIST_ROOT_ONLY || !r.isPersistable() ||
+                    ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET) != 0) &&
+                            activityNdx > 0) {
                 // Stop at first non-persistable or first break in task (CLEAR_WHEN_TASK_RESET).
                 break;
             }
@@ -679,10 +751,10 @@
         boolean rootHasReset = false;
         boolean askedCompatMode = false;
         int taskType = ActivityRecord.APPLICATION_ACTIVITY_TYPE;
-        boolean onTopOfHome = true;
         int userId = 0;
         String lastDescription = null;
         long lastTimeOnTop = 0;
+        boolean neverRelinquishIdentity = true;
         int taskId = -1;
         final int outerDepth = in.getDepth();
 
@@ -707,12 +779,12 @@
                 userId = Integer.valueOf(attrValue);
             } else if (ATTR_TASKTYPE.equals(attrName)) {
                 taskType = Integer.valueOf(attrValue);
-            } else if (ATTR_ONTOPOFHOME.equals(attrName)) {
-                onTopOfHome = Boolean.valueOf(attrValue);
             } else if (ATTR_LASTDESCRIPTION.equals(attrName)) {
                 lastDescription = attrValue;
             } else if (ATTR_LASTTIMEMOVED.equals(attrName)) {
                 lastTimeOnTop = Long.valueOf(attrValue);
+            } else if (ATTR_NEVERRELINQUISH.equals(attrName)) {
+                neverRelinquishIdentity = Boolean.valueOf(attrValue);
             } else {
                 Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName);
             }
@@ -746,8 +818,8 @@
 
         final TaskRecord task = new TaskRecord(stackSupervisor.mService, taskId, intent,
                 affinityIntent, affinity, realActivity, origActivity, rootHasReset,
-                askedCompatMode, taskType, onTopOfHome, userId, lastDescription, activities,
-                lastTimeOnTop);
+                askedCompatMode, taskType, userId, lastDescription, activities, lastTimeOnTop,
+                neverRelinquishIdentity);
 
         for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) {
             final ActivityRecord r = activities.get(activityNdx);
@@ -766,7 +838,7 @@
                     pw.print(" userId="); pw.print(userId);
                     pw.print(" taskType="); pw.print(taskType);
                     pw.print(" numFullscreen="); pw.print(numFullscreen);
-                    pw.print(" mOnTopOfHome="); pw.println(mOnTopOfHome);
+                    pw.print(" mTaskToReturnTo="); pw.println(mTaskToReturnTo);
         }
         if (affinity != null) {
             pw.print(prefix); pw.print("affinity="); pw.println(affinity);
diff --git a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
index b03c247..1332898 100644
--- a/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
+++ b/services/core/java/com/android/server/connectivity/NetworkAgentInfo.java
@@ -78,6 +78,6 @@
 
     public String name() {
         return "NetworkAgentInfo [" + networkInfo.getTypeName() + " (" +
-                networkInfo.getSubtypeName() + ")]";
+                networkInfo.getSubtypeName() + ") - " + network.toString() + "]";
     }
 }
diff --git a/services/core/java/com/android/server/display/DisplayDeviceInfo.java b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
index a77443d..c7f4f6a 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceInfo.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceInfo.java
@@ -119,7 +119,7 @@
     public int height;
 
     /**
-     * The refresh rate of the display.
+     * The refresh rate of the display, in frames per second.
      */
     public float refreshRate;
 
@@ -144,6 +144,20 @@
     public float yDpi;
 
     /**
+     * This is a positive value indicating the phase offset of the VSYNC events provided by
+     * Choreographer relative to the display refresh.  For example, if Choreographer reports
+     * that the refresh occurred at time N, it actually occurred at (N - appVsyncOffsetNanos).
+     */
+    public long appVsyncOffsetNanos;
+
+    /**
+     * This is how far in advance a buffer must be queued for presentation at
+     * a given time.  If you want a buffer to appear on the screen at
+     * time N, you must submit the buffer before (N - bufferDeadlineNanos).
+     */
+    public long presentationDeadlineNanos;
+
+    /**
      * Display flags.
      */
     public int flags;
@@ -219,6 +233,8 @@
                 && densityDpi == other.densityDpi
                 && xDpi == other.xDpi
                 && yDpi == other.yDpi
+                && appVsyncOffsetNanos == other.appVsyncOffsetNanos
+                && presentationDeadlineNanos == other.presentationDeadlineNanos
                 && flags == other.flags
                 && touch == other.touch
                 && rotation == other.rotation
@@ -242,6 +258,8 @@
         densityDpi = other.densityDpi;
         xDpi = other.xDpi;
         yDpi = other.yDpi;
+        appVsyncOffsetNanos = other.appVsyncOffsetNanos;
+        presentationDeadlineNanos = other.presentationDeadlineNanos;
         flags = other.flags;
         touch = other.touch;
         rotation = other.rotation;
@@ -261,6 +279,8 @@
         sb.append(", ").append(refreshRate).append(" fps, ");
         sb.append("density ").append(densityDpi);
         sb.append(", ").append(xDpi).append(" x ").append(yDpi).append(" dpi");
+        sb.append(", appVsyncOff ").append(appVsyncOffsetNanos);
+        sb.append(", presDeadline ").append(presentationDeadlineNanos);
         sb.append(", touch ").append(touchToString(touch));
         sb.append(", rotation ").append(rotation);
         sb.append(", type ").append(Display.typeToString(type));
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index e80aecd..098537c 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -161,6 +161,8 @@
                 mInfo.width = mPhys.width;
                 mInfo.height = mPhys.height;
                 mInfo.refreshRate = mPhys.refreshRate;
+                mInfo.appVsyncOffsetNanos = mPhys.appVsyncOffsetNanos;
+                mInfo.presentationDeadlineNanos = mPhys.presentationDeadlineNanos;
                 mInfo.state = mState;
 
                 // Assume that all built-in displays that have secure output (eg. HDCP) also
diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java
index d61a35b..ed619d9 100644
--- a/services/core/java/com/android/server/display/LogicalDisplay.java
+++ b/services/core/java/com/android/server/display/LogicalDisplay.java
@@ -213,6 +213,8 @@
             mBaseDisplayInfo.logicalDensityDpi = deviceInfo.densityDpi;
             mBaseDisplayInfo.physicalXDpi = deviceInfo.xDpi;
             mBaseDisplayInfo.physicalYDpi = deviceInfo.yDpi;
+            mBaseDisplayInfo.appVsyncOffsetNanos = deviceInfo.appVsyncOffsetNanos;
+            mBaseDisplayInfo.presentationDeadlineNanos = deviceInfo.presentationDeadlineNanos;
             mBaseDisplayInfo.state = deviceInfo.state;
             mBaseDisplayInfo.smallestNominalAppWidth = deviceInfo.width;
             mBaseDisplayInfo.smallestNominalAppHeight = deviceInfo.height;
diff --git a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
index bfd8372c..3b23b6a 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayAdapter.java
@@ -191,6 +191,7 @@
         private final int mWidth;
         private final int mHeight;
         private final float mRefreshRate;
+        private final long mDisplayPresentationDeadlineNanos;
         private final int mDensityDpi;
         private final boolean mSecure;
 
@@ -200,7 +201,7 @@
         private DisplayDeviceInfo mInfo;
 
         public OverlayDisplayDevice(IBinder displayToken, String name,
-                int width, int height, float refreshRate,
+                int width, int height, float refreshRate, long presentationDeadlineNanos,
                 int densityDpi, boolean secure, int state,
                 SurfaceTexture surfaceTexture) {
             super(OverlayDisplayAdapter.this, displayToken);
@@ -208,6 +209,7 @@
             mWidth = width;
             mHeight = height;
             mRefreshRate = refreshRate;
+            mDisplayPresentationDeadlineNanos = presentationDeadlineNanos;
             mDensityDpi = densityDpi;
             mSecure = secure;
             mState = state;
@@ -249,6 +251,8 @@
                 mInfo.densityDpi = mDensityDpi;
                 mInfo.xDpi = mDensityDpi;
                 mInfo.yDpi = mDensityDpi;
+                mInfo.presentationDeadlineNanos = mDisplayPresentationDeadlineNanos +
+                        1000000000L / (int) mRefreshRate;   // display's deadline + 1 frame
                 mInfo.flags = DisplayDeviceInfo.FLAG_PRESENTATION;
                 if (mSecure) {
                     mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
@@ -297,12 +301,13 @@
 
         // Called on the UI thread.
         @Override
-        public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate, int state) {
+        public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate,
+                long presentationDeadlineNanos, int state) {
             synchronized (getSyncRoot()) {
                 IBinder displayToken = SurfaceControl.createDisplay(mName, mSecure);
                 mDevice = new OverlayDisplayDevice(displayToken, mName,
-                        mWidth, mHeight, refreshRate, mDensityDpi, mSecure,
-                        state, surfaceTexture);
+                        mWidth, mHeight, refreshRate, presentationDeadlineNanos,
+                        mDensityDpi, mSecure, state, surfaceTexture);
 
                 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
             }
diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
index 06891f3..9ca5fda 100644
--- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java
+++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java
@@ -303,7 +303,7 @@
         public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture,
                 int width, int height) {
             mListener.onWindowCreated(surfaceTexture, mDefaultDisplayInfo.refreshRate,
-                    mDefaultDisplayInfo.state);
+                    mDefaultDisplayInfo.presentationDeadlineNanos, mDefaultDisplayInfo.state);
         }
 
         @Override
@@ -373,7 +373,7 @@
      */
     public interface Listener {
         public void onWindowCreated(SurfaceTexture surfaceTexture,
-                float refreshRate, int state);
+                float refreshRate, long presentationDeadlineNanos, int state);
         public void onWindowDestroyed();
         public void onStateChanged(int state);
     }
diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
index 14ef5a9..ec14cf1 100644
--- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java
@@ -171,6 +171,7 @@
                 mInfo.densityDpi = mDensityDpi;
                 mInfo.xDpi = mDensityDpi;
                 mInfo.yDpi = mDensityDpi;
+                mInfo.presentationDeadlineNanos = 1000000000L / (int) mInfo.refreshRate; // 1 frame
                 mInfo.flags = 0;
                 if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
                     mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
diff --git a/services/core/java/com/android/server/display/WifiDisplayAdapter.java b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
index cd57941..a05bf2c 100644
--- a/services/core/java/com/android/server/display/WifiDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/WifiDisplayAdapter.java
@@ -127,7 +127,7 @@
         pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast);
         pw.println("mPendingNotificationUpdate=" + mPendingNotificationUpdate);
         pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers);
- 
+
         // Try to dump the controller state.
         if (mDisplayController == null) {
             pw.println("mDisplayController=null");
@@ -729,6 +729,7 @@
                 mInfo.width = mWidth;
                 mInfo.height = mHeight;
                 mInfo.refreshRate = mRefreshRate;
+                mInfo.presentationDeadlineNanos = 1000000000L / (int) mRefreshRate; // 1 frame
                 mInfo.flags = mFlags;
                 mInfo.type = Display.TYPE_WIFI;
                 mInfo.address = mAddress;
diff --git a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
index 23cf40b..74eaf2a 100644
--- a/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
+++ b/services/core/java/com/android/server/hdmi/ActiveSourceHandler.java
@@ -72,9 +72,9 @@
         }
 
         if (!mSource.isInPresetInstallationMode()) {
-            int prevActiveInput = mSource.getActiveInput();
+            int prevActiveInput = mSource.getActivePortId();
             mSource.updateActiveDevice(deviceLogicalAddress, routingPath);
-            if (prevActiveInput != mSource.getActiveInput()) {
+            if (prevActiveInput != mSource.getActivePortId()) {
                 // TODO: change port input here.
             }
             invokeCallback(HdmiCec.RESULT_SUCCESS);
diff --git a/services/core/java/com/android/server/hdmi/HdmiAnnotations.java b/services/core/java/com/android/server/hdmi/HdmiAnnotations.java
new file mode 100644
index 0000000..969595d
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/HdmiAnnotations.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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.hdmi;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation container for Hdmi control service package.
+ */
+public class HdmiAnnotations {
+    /**
+     * Annotation type to used to mark a method which should be run on service thread.
+     * This annotation should go with {@code assertRunOnServiceThread} used to verify
+     * whether it's called from service thread.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.METHOD)
+    public @interface ServiceThreadOnly {
+    }
+
+    /**
+     * Annotation type to used to mark a method which should be run on io thread.
+     * This annotation should go with {@code assertRunOnIoThread} used to verify
+     * whether it's called from io thread.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.METHOD)
+    public @interface IoThreadOnly {
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecController.java b/services/core/java/com/android/server/hdmi/HdmiCecController.java
index 6d05e82..4fbe845 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecController.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecController.java
@@ -26,6 +26,8 @@
 import android.util.SparseArray;
 
 import com.android.internal.util.Predicate;
+import com.android.server.hdmi.HdmiAnnotations.IoThreadOnly;
+import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 import com.android.server.hdmi.HdmiControlService.DevicePollingCallback;
 
 import libcore.util.EmptyArray;
@@ -144,7 +146,9 @@
         mNativePtr = nativePtr;
     }
 
+    @ServiceThreadOnly
     void addLocalDevice(int deviceType, HdmiCecLocalDevice device) {
+        assertRunOnServiceThread();
         mLocalDevices.put(deviceType, device);
     }
 
@@ -161,6 +165,7 @@
      *                         Otherwise, scan address will start from {@code preferredAddress}
      * @param callback callback interface to report allocated logical address to caller
      */
+    @ServiceThreadOnly
     void allocateLogicalAddress(final int deviceType, final int preferredAddress,
             final AllocateAddressCallback callback) {
         assertRunOnServiceThread();
@@ -173,6 +178,7 @@
         });
     }
 
+    @IoThreadOnly
     private void handleAllocateLogicalAddress(final int deviceType, int preferredAddress,
             final AllocateAddressCallback callback) {
         assertRunOnIoThread();
@@ -245,6 +251,7 @@
      * @param newLogicalAddress a logical address to be added
      * @return 0 on success. Otherwise, returns negative value
      */
+    @ServiceThreadOnly
     int addLogicalAddress(int newLogicalAddress) {
         assertRunOnServiceThread();
         if (HdmiCec.isValidAddress(newLogicalAddress)) {
@@ -259,6 +266,7 @@
      *
      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
      */
+    @ServiceThreadOnly
     void clearLogicalAddress() {
         assertRunOnServiceThread();
         // TODO: consider to backup logical address so that new logical address
@@ -277,6 +285,7 @@
      * @return CEC physical address of the device. The range of success address
      *         is between 0x0000 and 0xFFFF. If failed it returns -1
      */
+    @ServiceThreadOnly
     int getPhysicalAddress() {
         assertRunOnServiceThread();
         return nativeGetPhysicalAddress(mNativePtr);
@@ -287,6 +296,7 @@
      *
      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
      */
+    @ServiceThreadOnly
     int getVersion() {
         assertRunOnServiceThread();
         return nativeGetVersion(mNativePtr);
@@ -297,6 +307,7 @@
      *
      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
      */
+    @ServiceThreadOnly
     int getVendorId() {
         assertRunOnServiceThread();
         return nativeGetVendorId(mNativePtr);
@@ -311,6 +322,7 @@
      * @param value a value of option. Actual value varies flag. For more
      *        details, look at description of flags
      */
+    @ServiceThreadOnly
     void setOption(int flag, int value) {
         assertRunOnServiceThread();
         nativeSetOption(mNativePtr, flag, value);
@@ -321,6 +333,7 @@
      *
      * @param enabled whether to enable/disable ARC
      */
+    @ServiceThreadOnly
     void setAudioReturnChannel(boolean enabled) {
         assertRunOnServiceThread();
         nativeSetAudioReturnChannel(mNativePtr, enabled);
@@ -332,6 +345,7 @@
      * @param port port number to check connection status
      * @return true if connected; otherwise, return false
      */
+    @ServiceThreadOnly
     boolean isConnected(int port) {
         assertRunOnServiceThread();
         return nativeIsConnected(mNativePtr, port);
@@ -347,6 +361,7 @@
      * @param pickStrategy strategy how to pick polling candidates
      * @param retryCount the number of retry used to send polling message to remote devices
      */
+    @ServiceThreadOnly
     void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
         assertRunOnServiceThread();
 
@@ -360,6 +375,7 @@
      *
      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
      */
+    @ServiceThreadOnly
     List<HdmiCecLocalDevice> getLocalDeviceList() {
         assertRunOnServiceThread();
         return HdmiUtils.sparseArrayToList(mLocalDevices);
@@ -400,7 +416,9 @@
         return pollingCandidates;
     }
 
+    @ServiceThreadOnly
     private boolean isAllocatedLocalDeviceAddress(int address) {
+        assertRunOnServiceThread();
         for (int i = 0; i < mLocalDevices.size(); ++i) {
             if (mLocalDevices.valueAt(i).isAddressOf(address)) {
                 return true;
@@ -409,6 +427,7 @@
         return false;
     }
 
+    @ServiceThreadOnly
     private void runDevicePolling(final List<Integer> candidates, final int retryCount,
             final DevicePollingCallback callback) {
         assertRunOnServiceThread();
@@ -433,6 +452,7 @@
         });
     }
 
+    @IoThreadOnly
     private boolean sendPollMessage(int address, int retryCount) {
         assertRunOnIoThread();
         for (int i = 0; i < retryCount; ++i) {
@@ -480,6 +500,7 @@
         return isAllocatedLocalDeviceAddress(address);
     }
 
+    @ServiceThreadOnly
     private void onReceiveCommand(HdmiCecMessage message) {
         assertRunOnServiceThread();
         if (isAcceptableAddress(message.getDestination())
@@ -493,16 +514,20 @@
             HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildFeatureAbortCommand(
                     sourceAddress, message.getSource(), message.getOpcode(),
                     HdmiConstants.ABORT_REFUSED);
-            sendCommand(cecMessage, null);
+            sendCommand(cecMessage);
         }
     }
 
+    @ServiceThreadOnly
     void sendCommand(HdmiCecMessage cecMessage) {
+        assertRunOnServiceThread();
         sendCommand(cecMessage, null);
     }
 
+    @ServiceThreadOnly
     void sendCommand(final HdmiCecMessage cecMessage,
             final HdmiControlService.SendMessageCallback callback) {
+        assertRunOnServiceThread();
         runOnIoThread(new Runnable() {
             @Override
             public void run() {
@@ -527,6 +552,7 @@
     /**
      * Called by native when incoming CEC message arrived.
      */
+    @ServiceThreadOnly
     private void handleIncomingCecCommand(int srcAddress, int dstAddress, byte[] body) {
         assertRunOnServiceThread();
         onReceiveCommand(HdmiCecMessageBuilder.of(srcAddress, dstAddress, body));
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index 08d7786..6f7f5c2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -23,6 +23,7 @@
 import android.util.Slog;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 
 import java.util.ArrayList;
 import java.util.Iterator;
@@ -87,7 +88,9 @@
         }
     }
 
+    @ServiceThreadOnly
     void init() {
+        assertRunOnServiceThread();
         mPreferredAddress = HdmiCec.ADDR_UNREGISTERED;
         // TODO: load preferred address from permanent storage.
     }
@@ -103,9 +106,9 @@
      * @param message incoming message
      * @return true if consumed a message; otherwise, return false.
      */
+    @ServiceThreadOnly
     final boolean dispatchMessage(HdmiCecMessage message) {
         assertRunOnServiceThread();
-
         int dest = message.getDestination();
         if (dest != mAddress && dest != HdmiCec.ADDR_BROADCAST) {
             return false;
@@ -115,9 +118,9 @@
         return onMessage(message);
     }
 
+    @ServiceThreadOnly
     protected final boolean onMessage(HdmiCecMessage message) {
         assertRunOnServiceThread();
-
         if (dispatchMessageToAction(message)) {
             return true;
         }
@@ -147,7 +150,9 @@
         }
     }
 
+    @ServiceThreadOnly
     private boolean dispatchMessageToAction(HdmiCecMessage message) {
+        assertRunOnServiceThread();
         for (FeatureAction action : mActions) {
             if (action.processCommand(message)) {
                 return true;
@@ -156,6 +161,7 @@
         return false;
     }
 
+    @ServiceThreadOnly
     protected boolean handleGivePhysicalAddress() {
         assertRunOnServiceThread();
 
@@ -166,9 +172,9 @@
         return true;
     }
 
+    @ServiceThreadOnly
     protected boolean handleGiveDeviceVendorId() {
         assertRunOnServiceThread();
-
         int vendorId = mService.getVendorId();
         HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildDeviceVendorIdCommand(
                 mAddress, vendorId);
@@ -176,9 +182,9 @@
         return true;
     }
 
+    @ServiceThreadOnly
     protected boolean handleGetCecVersion(HdmiCecMessage message) {
         assertRunOnServiceThread();
-
         int version = mService.getCecVersion();
         HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildCecVersion(message.getDestination(),
                 message.getSource(), version);
@@ -186,9 +192,9 @@
         return true;
     }
 
+    @ServiceThreadOnly
     protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
         assertRunOnServiceThread();
-
         Slog.w(TAG, "Only TV can handle <Get Menu Language>:" + message.toString());
         mService.sendCecCommand(
                 HdmiCecMessageBuilder.buildFeatureAbortCommand(mAddress,
@@ -197,9 +203,9 @@
         return true;
     }
 
+    @ServiceThreadOnly
     protected boolean handleGiveOsdName(HdmiCecMessage message) {
         assertRunOnServiceThread();
-
         // Note that since this method is called after logical address allocation is done,
         // mDeviceInfo should not be null.
         HdmiCecMessage cecMessage = HdmiCecMessageBuilder.buildSetOsdNameCommand(
@@ -236,45 +242,52 @@
         return false;
     }
 
+    @ServiceThreadOnly
     final void handleAddressAllocated(int logicalAddress) {
         assertRunOnServiceThread();
-
         mAddress = mPreferredAddress = logicalAddress;
         onAddressAllocated(logicalAddress);
     }
 
+    @ServiceThreadOnly
     HdmiCecDeviceInfo getDeviceInfo() {
         assertRunOnServiceThread();
         return mDeviceInfo;
     }
 
+    @ServiceThreadOnly
     void setDeviceInfo(HdmiCecDeviceInfo info) {
         assertRunOnServiceThread();
         mDeviceInfo = info;
     }
 
     // Returns true if the logical address is same as the argument.
+    @ServiceThreadOnly
     boolean isAddressOf(int addr) {
         assertRunOnServiceThread();
         return addr == mAddress;
     }
 
     // Resets the logical address to unregistered(15), meaning the logical device is invalid.
+    @ServiceThreadOnly
     void clearAddress() {
         assertRunOnServiceThread();
         mAddress = HdmiCec.ADDR_UNREGISTERED;
     }
 
+    @ServiceThreadOnly
     void setPreferredAddress(int addr) {
         assertRunOnServiceThread();
         mPreferredAddress = addr;
     }
 
+    @ServiceThreadOnly
     int getPreferredAddress() {
         assertRunOnServiceThread();
         return mPreferredAddress;
     }
 
+    @ServiceThreadOnly
     void addAndStartAction(final FeatureAction action) {
         assertRunOnServiceThread();
         mActions.add(action);
@@ -282,6 +295,7 @@
     }
 
     // See if we have an action of a given type in progress.
+    @ServiceThreadOnly
     <T extends FeatureAction> boolean hasAction(final Class<T> clazz) {
         assertRunOnServiceThread();
         for (FeatureAction action : mActions) {
@@ -293,6 +307,7 @@
     }
 
     // Returns all actions matched with given class type.
+    @ServiceThreadOnly
     <T extends FeatureAction> List<T> getActions(final Class<T> clazz) {
         assertRunOnServiceThread();
         ArrayList<T> actions = new ArrayList<>();
@@ -309,17 +324,21 @@
      *
      * @param action {@link FeatureAction} to remove
      */
+    @ServiceThreadOnly
     void removeAction(final FeatureAction action) {
         assertRunOnServiceThread();
         mActions.remove(action);
     }
 
     // Remove all actions matched with the given Class type.
+    @ServiceThreadOnly
     <T extends FeatureAction> void removeAction(final Class<T> clazz) {
+        assertRunOnServiceThread();
         removeActionExcept(clazz, null);
     }
 
     // Remove all actions matched with the given Class type besides |exception|.
+    @ServiceThreadOnly
     <T extends FeatureAction> void removeActionExcept(final Class<T> clazz,
             final FeatureAction exception) {
         assertRunOnServiceThread();
@@ -352,7 +371,9 @@
         return mService;
     }
 
+    @ServiceThreadOnly
     final boolean isConnectedToArcPort(int path) {
+        assertRunOnServiceThread();
         return mService.isConnectedToArcPort(path);
     }
 
@@ -372,15 +393,28 @@
     }
 
     /**
-     * Returns the ID of the active HDMI port. The active input is the port that has the active
-     * routing path connected directly or indirectly under the device hierarchy.
+     * Returns the ID of the active HDMI port. The active port is the one that has the active
+     * routing path connected to it directly or indirectly under the device hierarchy.
      */
-    int getActiveInput() {
+    int getActivePortId() {
         synchronized (mLock) {
             return mService.pathToPortId(mActiveRoutingPath);
         }
     }
 
+    /**
+     * Update the active port.
+     *
+     * @param portId the new active port id
+     */
+    void setActivePortId(int portId) {
+        synchronized (mLock) {
+            // We update active routing path instead, since we get the active port id from
+            // the active routing path.
+            mActiveRoutingPath = mService.portIdToPath(portId);
+        }
+    }
+
     void updateActiveDevice(int logicalAddress, int physicalAddress) {
         synchronized (mLock) {
             mActiveSource = logicalAddress;
@@ -429,11 +463,13 @@
         }
     }
 
+    @ServiceThreadOnly
     HdmiCecMessageCache getCecMessageCache() {
         assertRunOnServiceThread();
         return mCecMessageCache;
     }
 
+    @ServiceThreadOnly
     int pathToPortId(int newPath) {
         assertRunOnServiceThread();
         return mService.pathToPortId(newPath);
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index 01345ef..d7e36b3 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -21,6 +21,8 @@
 import android.os.RemoteException;
 import android.util.Slog;
 
+import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
+
 /**
  * Represent a logical device of type Playback residing in Android system.
  */
@@ -32,11 +34,14 @@
     }
 
     @Override
+    @ServiceThreadOnly
     protected void onAddressAllocated(int logicalAddress) {
+        assertRunOnServiceThread();
         mService.sendCecCommand(HdmiCecMessageBuilder.buildReportPhysicalAddressCommand(
                 mAddress, mService.getPhysicalAddress(), mDeviceType));
     }
 
+    @ServiceThreadOnly
     void oneTouchPlay(IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         if (hasAction(OneTouchPlayAction.class)) {
@@ -56,6 +61,7 @@
         addAndStartAction(action);
     }
 
+    @ServiceThreadOnly
     void queryDisplayStatus(IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         if (hasAction(DevicePowerStatusAction.class)) {
@@ -73,7 +79,9 @@
         addAndStartAction(action);
     }
 
+    @ServiceThreadOnly
     private void invokeCallback(IHdmiControlCallback callback, int result) {
+        assertRunOnServiceThread();
         try {
             callback.onComplete(result);
         } catch (RemoteException e) {
@@ -82,7 +90,9 @@
     }
 
     @Override
+    @ServiceThreadOnly
     void onHotplug(int portId, boolean connected) {
+        assertRunOnServiceThread();
         // TODO: clear devices connected to the given port id.
         mCecMessageCache.flushAll();
     }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 92ddd3d..2b53895 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -26,6 +26,7 @@
 
 import com.android.internal.annotations.GuardedBy;
 import com.android.server.hdmi.DeviceDiscoveryAction.DeviceDiscoveryCallback;
+import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -42,19 +43,31 @@
     @GuardedBy("mLock")
     private boolean mArcStatusEnabled = false;
 
-    @GuardedBy("mLock")
     // Whether SystemAudioMode is "On" or not.
+    @GuardedBy("mLock")
     private boolean mSystemAudioMode;
 
+
+    // Copy of mDeviceInfos to guarantee thread-safety.
+    @GuardedBy("mLock")
+    private List<HdmiCecDeviceInfo> mSafeAllDeviceInfos = Collections.emptyList();
+    // All external cec device which excludes local devices.
+    @GuardedBy("mLock")
+    private List<HdmiCecDeviceInfo> mSafeExternalDeviceInfos = Collections.emptyList();
+
     // Map-like container of all cec devices including local ones.
     // A logical address of device is used as key of container.
+    // This is not thread-safe. For external purpose use mSafeDeviceInfos.
     private final SparseArray<HdmiCecDeviceInfo> mDeviceInfos = new SparseArray<>();
 
     HdmiCecLocalDeviceTv(HdmiControlService service) {
         super(service, HdmiCec.DEVICE_TV);
+
+        // TODO: load system audio mode and set it to mSystemAudioMode.
     }
 
     @Override
+    @ServiceThreadOnly
     protected void onAddressAllocated(int logicalAddress) {
         assertRunOnServiceThread();
         // TODO: vendor-specific initialization here.
@@ -65,7 +78,7 @@
                 mAddress, mService.getVendorId()));
 
         launchDeviceDiscovery();
-        // TODO: Start routing control action, device discovery action.
+        // TODO: Start routing control action
     }
 
     /**
@@ -74,9 +87,10 @@
      * @param targetAddress logical address of the device to select
      * @param callback callback object to report the result with
      */
+    @ServiceThreadOnly
     void deviceSelect(int targetAddress, IHdmiControlCallback callback) {
         assertRunOnServiceThread();
-        HdmiCecDeviceInfo targetDevice = mService.getDeviceInfo(targetAddress);
+        HdmiCecDeviceInfo targetDevice = getDeviceInfo(targetAddress);
         if (targetDevice == null) {
             invokeCallback(callback, HdmiCec.RESULT_TARGET_NOT_AVAILABLE);
             return;
@@ -85,7 +99,66 @@
         addAndStartAction(new DeviceSelectAction(this, targetDevice, callback));
     }
 
+    /**
+     * Performs the action routing control.
+     *
+     * @param portId new HDMI port to route to
+     * @param callback callback object to report the result with
+     */
+    @ServiceThreadOnly
+    void portSelect(int portId, IHdmiControlCallback callback) {
+        assertRunOnServiceThread();
+        if (isInPresetInstallationMode()) {
+            invokeCallback(callback, HdmiCec.RESULT_INCORRECT_MODE);
+            return;
+        }
+        // Make sure this call does not stem from <Active Source> message reception, in
+        // which case the two ports will be the same.
+        if (portId == getActivePortId()) {
+            invokeCallback(callback, HdmiCec.RESULT_SUCCESS);
+            return;
+        }
+        setActivePortId(portId);
+
+        // TODO: Return immediately if the operation is triggered by <Text/Image View On>
+        // TODO: Handle invalid port id / active input which should be treated as an
+        //        internal tuner.
+
+        removeAction(RoutingControlAction.class);
+
+        int oldPath = mService.portIdToPath(mService.portIdToPath(getActivePortId()));
+        int newPath = mService.portIdToPath(portId);
+        HdmiCecMessage routingChange =
+                HdmiCecMessageBuilder.buildRoutingChange(mAddress, oldPath, newPath);
+        mService.sendCecCommand(routingChange);
+        addAndStartAction(new RoutingControlAction(this, newPath, callback));
+    }
+
+    /**
+     * Sends key to a target CEC device.
+     *
+     * @param keyCode key code to send. Defined in {@link android.view.KeyEvent}.
+     * @param isPressed true if this is keypress event
+     */
+    @ServiceThreadOnly
+    void sendKeyEvent(int keyCode, boolean isPressed) {
+        assertRunOnServiceThread();
+        List<SendKeyAction> action = getActions(SendKeyAction.class);
+        if (!action.isEmpty()) {
+            action.get(0).processKeyEvent(keyCode, isPressed);
+        } else {
+            if (isPressed) {
+                addAndStartAction(new SendKeyAction(this, getActiveSource(), keyCode));
+            } else {
+                Slog.w(TAG, "Discard key release event");
+            }
+        }
+    }
+
     private static void invokeCallback(IHdmiControlCallback callback, int result) {
+        if (callback == null) {
+            return;
+        }
         try {
             callback.onComplete(result);
         } catch (RemoteException e) {
@@ -94,6 +167,7 @@
     }
 
     @Override
+    @ServiceThreadOnly
     protected boolean handleGetMenuLanguage(HdmiCecMessage message) {
         assertRunOnServiceThread();
         HdmiCecMessage command = HdmiCecMessageBuilder.buildSetMenuLanguageCommand(
@@ -108,6 +182,7 @@
     }
 
     @Override
+    @ServiceThreadOnly
     protected boolean handleReportPhysicalAddress(HdmiCecMessage message) {
         assertRunOnServiceThread();
         // Ignore if [Device Discovery Action] is going on.
@@ -130,6 +205,7 @@
     }
 
     @Override
+    @ServiceThreadOnly
     protected boolean handleVendorSpecificCommand(HdmiCecMessage message) {
         assertRunOnServiceThread();
         List<VendorSpecificAction> actions = Collections.emptyList();
@@ -155,6 +231,7 @@
         return true;
     }
 
+    @ServiceThreadOnly
     private void launchDeviceDiscovery() {
         assertRunOnServiceThread();
         clearDeviceInfoList();
@@ -174,15 +251,39 @@
                         }
 
                         addAndStartAction(new HotplugDetectionAction(HdmiCecLocalDeviceTv.this));
+
+                        // If there is AVR, initiate System Audio Auto initiation action,
+                        // which turns on and off system audio according to last system
+                        // audio setting.
+                        HdmiCecDeviceInfo avrInfo = getAvrDeviceInfo();
+                        if (avrInfo != null) {
+                            addAndStartAction(new SystemAudioAutoInitiationAction(
+                                    HdmiCecLocalDeviceTv.this, avrInfo.getLogicalAddress()));
+                        }
                     }
                 });
         addAndStartAction(action);
     }
 
     // Clear all device info.
+    @ServiceThreadOnly
     private void clearDeviceInfoList() {
         assertRunOnServiceThread();
         mDeviceInfos.clear();
+        updateSafeDeviceInfoList();
+    }
+
+    @ServiceThreadOnly
+    void changeSystemAudioMode(boolean enabled, IHdmiControlCallback callback) {
+        assertRunOnServiceThread();
+        HdmiCecDeviceInfo avr = getAvrDeviceInfo();
+        if (avr == null) {
+            invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
+            return;
+        }
+
+        addAndStartAction(
+                new SystemAudioActionFromTv(this, avr.getLogicalAddress(), enabled, callback));
     }
 
     void setSystemAudioMode(boolean on) {
@@ -190,15 +291,13 @@
             if (on != mSystemAudioMode) {
                 mSystemAudioMode = on;
                 // TODO: Need to set the preference for SystemAudioMode.
-                // TODO: Need to handle the notification of changing the mode and
-                // to identify the notification should be handled in the service or TvSettings.
+                mService.announceSystemAudioModeChange(on);
             }
         }
     }
 
     boolean getSystemAudioMode() {
         synchronized (mLock) {
-            assertRunOnServiceThread();
             return mSystemAudioMode;
         }
     }
@@ -231,11 +330,13 @@
         }
     }
 
+    @ServiceThreadOnly
     void setAudioStatus(boolean mute, int volume) {
         mService.setAudioStatus(mute, volume);
     }
 
     @Override
+    @ServiceThreadOnly
     protected boolean handleInitiateArc(HdmiCecMessage message) {
         assertRunOnServiceThread();
         // In case where <Initiate Arc> is started by <Request ARC Initiation>
@@ -248,6 +349,7 @@
     }
 
     @Override
+    @ServiceThreadOnly
     protected boolean handleTerminateArc(HdmiCecMessage message) {
         assertRunOnServiceThread();
         // In case where <Terminate Arc> is started by <Request ARC Termination>
@@ -262,18 +364,20 @@
     }
 
     @Override
+    @ServiceThreadOnly
     protected boolean handleSetSystemAudioMode(HdmiCecMessage message) {
         assertRunOnServiceThread();
         if (!isMessageForSystemAudio(message)) {
             return false;
         }
         SystemAudioActionFromAvr action = new SystemAudioActionFromAvr(this,
-                message.getSource(), HdmiUtils.parseCommandParamSystemAudioStatus(message));
+                message.getSource(), HdmiUtils.parseCommandParamSystemAudioStatus(message), null);
         addAndStartAction(action);
         return true;
     }
 
     @Override
+    @ServiceThreadOnly
     protected boolean handleSystemAudioModeStatus(HdmiCecMessage message) {
         assertRunOnServiceThread();
         if (!isMessageForSystemAudio(message)) {
@@ -303,6 +407,7 @@
      * @return {@code null} if it is new device. Otherwise, returns old {@HdmiCecDeviceInfo}
      *         that has the same logical address as new one has.
      */
+    @ServiceThreadOnly
     HdmiCecDeviceInfo addDeviceInfo(HdmiCecDeviceInfo deviceInfo) {
         assertRunOnServiceThread();
         HdmiCecDeviceInfo oldDeviceInfo = getDeviceInfo(deviceInfo.getLogicalAddress());
@@ -310,6 +415,7 @@
             removeDeviceInfo(deviceInfo.getLogicalAddress());
         }
         mDeviceInfos.append(deviceInfo.getLogicalAddress(), deviceInfo);
+        updateSafeDeviceInfoList();
         return oldDeviceInfo;
     }
 
@@ -322,12 +428,14 @@
      * @param logicalAddress logical address of device to be removed
      * @return removed {@link HdmiCecDeviceInfo} it exists. Otherwise, returns {@code null}
      */
+    @ServiceThreadOnly
     HdmiCecDeviceInfo removeDeviceInfo(int logicalAddress) {
         assertRunOnServiceThread();
         HdmiCecDeviceInfo deviceInfo = mDeviceInfos.get(logicalAddress);
         if (deviceInfo != null) {
             mDeviceInfos.remove(logicalAddress);
         }
+        updateSafeDeviceInfoList();
         return deviceInfo;
     }
 
@@ -335,13 +443,14 @@
      * Return a list of all {@link HdmiCecDeviceInfo}.
      *
      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+     * This is not thread-safe. For thread safety, call {@link #getSafeDeviceInfoList(boolean)}.
      */
+    @ServiceThreadOnly
     List<HdmiCecDeviceInfo> getDeviceInfoList(boolean includelLocalDevice) {
         assertRunOnServiceThread();
         if (includelLocalDevice) {
-                return HdmiUtils.sparseArrayToList(mDeviceInfos);
+            return HdmiUtils.sparseArrayToList(mDeviceInfos);
         } else {
-
             ArrayList<HdmiCecDeviceInfo> infoList = new ArrayList<>();
             for (int i = 0; i < mDeviceInfos.size(); ++i) {
                 HdmiCecDeviceInfo info = mDeviceInfos.valueAt(i);
@@ -353,6 +462,33 @@
         }
     }
 
+    /**
+     * Return a list of  {@link HdmiCecDeviceInfo}.
+     *
+     * @param includeLocalDevice whether to include local device in result.
+     */
+    List<HdmiCecDeviceInfo> getSafeDeviceInfoList(boolean includeLocalDevice) {
+        synchronized (mLock) {
+            if (includeLocalDevice) {
+                return mSafeAllDeviceInfos;
+            } else {
+                return mSafeExternalDeviceInfos;
+            }
+        }
+    }
+
+    @ServiceThreadOnly
+    private void updateSafeDeviceInfoList() {
+        assertRunOnServiceThread();
+        List<HdmiCecDeviceInfo> copiedDevices = HdmiUtils.sparseArrayToList(mDeviceInfos);
+        List<HdmiCecDeviceInfo> externalDeviceInfos = getDeviceInfoList(false);
+        synchronized (mLock) {
+            mSafeAllDeviceInfos = copiedDevices;
+            mSafeExternalDeviceInfos = externalDeviceInfos;
+        }
+    }
+
+    @ServiceThreadOnly
     private boolean isLocalDeviceAddress(int address) {
         assertRunOnServiceThread();
         for (HdmiCecLocalDevice device : mService.getAllLocalDevices()) {
@@ -363,23 +499,47 @@
         return false;
     }
 
+    @ServiceThreadOnly
+    HdmiCecDeviceInfo getAvrDeviceInfo() {
+        assertRunOnServiceThread();
+        return getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
+    }
+
     /**
      * Return a {@link HdmiCecDeviceInfo} corresponding to the given {@code logicalAddress}.
      *
      * <p>Declared as package-private. accessed by {@link HdmiControlService} only.
+     * This is not thread-safe. For thread safety, call {@link #getSafeDeviceInfo(int)}.
      *
      * @param logicalAddress logical address to be retrieved
      * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
      *         Returns null if no logical address matched
      */
+    @ServiceThreadOnly
     HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
         assertRunOnServiceThread();
         return mDeviceInfos.get(logicalAddress);
     }
 
-    HdmiCecDeviceInfo getAvrDeviceInfo() {
-        assertRunOnServiceThread();
-        return getDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
+    boolean hasSystemAudioDevice() {
+        return getSafeAvrDeviceInfo() != null;
+    }
+
+    HdmiCecDeviceInfo getSafeAvrDeviceInfo() {
+        return getSafeDeviceInfo(HdmiCec.ADDR_AUDIO_SYSTEM);
+    }
+
+    /**
+     * Thread safe version of {@link #getDeviceInfo(int)}.
+     *
+     * @param logicalAddress logical address to be retrieved
+     * @return {@link HdmiCecDeviceInfo} matched with the given {@code logicalAddress}.
+     *         Returns null if no logical address matched
+     */
+    HdmiCecDeviceInfo getSafeDeviceInfo(int logicalAddress) {
+        synchronized (mLock) {
+            return mSafeAllDeviceInfos.get(logicalAddress);
+        }
     }
 
     /**
@@ -387,11 +547,15 @@
      *
      * @param info device info of a new device.
      */
+    @ServiceThreadOnly
     final void addCecDevice(HdmiCecDeviceInfo info) {
         assertRunOnServiceThread();
         addDeviceInfo(info);
-
-        // TODO: announce new device detection.
+        if (info.getLogicalAddress() == mAddress) {
+            // The addition of TV device itself should not be notified.
+            return;
+        }
+        mService.invokeDeviceEventListeners(info, true);
     }
 
     /**
@@ -399,12 +563,12 @@
      *
      * @param address a logical address of a device to be removed
      */
+    @ServiceThreadOnly
     final void removeCecDevice(int address) {
         assertRunOnServiceThread();
-        removeDeviceInfo(address);
+        HdmiCecDeviceInfo info = removeDeviceInfo(address);
         mCecMessageCache.flushMessagesFrom(address);
-
-        // TODO: announce a device removal.
+        mService.invokeDeviceEventListeners(info, false);
     }
 
     /**
@@ -415,6 +579,7 @@
      * @param path routing path or physical address
      * @return {@link HdmiCecDeviceInfo} if the matched info is found; otherwise null
      */
+    @ServiceThreadOnly
     final HdmiCecDeviceInfo getDeviceInfoByPath(int path) {
         assertRunOnServiceThread();
         for (HdmiCecDeviceInfo info : getDeviceInfoList(false)) {
@@ -434,6 +599,7 @@
      * @param logicalAddress logical address of a device to be searched
      * @return true if exist; otherwise false
      */
+    @ServiceThreadOnly
     boolean isInDeviceList(int physicalAddress, int logicalAddress) {
         assertRunOnServiceThread();
         HdmiCecDeviceInfo device = getDeviceInfo(logicalAddress);
@@ -444,6 +610,7 @@
     }
 
     @Override
+    @ServiceThreadOnly
     void onHotplug(int portNo, boolean connected) {
         assertRunOnServiceThread();
         // TODO: delegate onHotplug event to each local device.
@@ -456,4 +623,10 @@
             hotplugActions.get(0).pollAllDevicesNow();
         }
     }
+
+    boolean canChangeSystemAudio() {
+        // TODO: implement this.
+        // return true if no system audio control sequence is running.
+        return false;
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
index 8dbfd85..361a063 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecMessageBuilder.java
@@ -377,6 +377,17 @@
         return buildCommand(src, dest, HdmiCec.MESSAGE_USER_CONTROL_RELEASED);
     }
 
+    /**
+     * Build &lt;Give System Audio Mode Status&gt; command.
+     *
+     * @param src source address of command
+     * @param dest destination address of command
+     * @return newly created {@link HdmiCecMessage}
+     */
+    static HdmiCecMessage buildGiveSystemAudioModeStatus(int src, int dest) {
+        return buildCommand(src, dest, HdmiCec.MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS);
+    }
+
     /***** Please ADD new buildXXX() methods above. ******/
 
     /**
diff --git a/services/core/java/com/android/server/hdmi/HdmiConstants.java b/services/core/java/com/android/server/hdmi/HdmiConstants.java
index 9c60e55..5294506 100644
--- a/services/core/java/com/android/server/hdmi/HdmiConstants.java
+++ b/services/core/java/com/android/server/hdmi/HdmiConstants.java
@@ -95,5 +95,7 @@
     static final int POLL_ITERATION_IN_ORDER = 0x10000;
     static final int POLL_ITERATION_REVERSE_ORDER = 0x20000;
 
+    static final int UNKNOWN_VOLUME = -1;
+
     private HdmiConstants() { /* cannot be instantiated */ }
 }
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 10da756..1a18c10 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -27,6 +27,7 @@
 import android.hardware.hdmi.IHdmiControlService;
 import android.hardware.hdmi.IHdmiDeviceEventListener;
 import android.hardware.hdmi.IHdmiHotplugEventListener;
+import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener;
 import android.os.Build;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -37,7 +38,9 @@
 import android.util.SparseArray;
 import android.util.SparseIntArray;
 
+import com.android.internal.annotations.GuardedBy;
 import com.android.server.SystemService;
+import com.android.server.hdmi.HdmiAnnotations.ServiceThreadOnly;
 import com.android.server.hdmi.HdmiCecController.AllocateAddressCallback;
 
 import java.util.ArrayList;
@@ -94,21 +97,33 @@
 
     // List of listeners registered by callers that want to get notified of
     // hotplug events.
+    @GuardedBy("mLock")
     private final ArrayList<IHdmiHotplugEventListener> mHotplugEventListeners = new ArrayList<>();
 
     // List of records for hotplug event listener to handle the the caller killed in action.
+    @GuardedBy("mLock")
     private final ArrayList<HotplugEventListenerRecord> mHotplugEventListenerRecords =
             new ArrayList<>();
 
     // List of listeners registered by callers that want to get notified of
     // device status events.
+    @GuardedBy("mLock")
     private final ArrayList<IHdmiDeviceEventListener> mDeviceEventListeners = new ArrayList<>();
 
     // List of records for device event listener to handle the the caller killed in action.
+    @GuardedBy("mLock")
     private final ArrayList<DeviceEventListenerRecord> mDeviceEventListenerRecords =
             new ArrayList<>();
 
-    // Handler running on service thread. It's used to run a task in service thread.
+    // List of listeners registered by callers that want to get notified of
+    // system audio mode changes.
+    private final ArrayList<IHdmiSystemAudioModeChangeListener>
+            mSystemAudioModeChangeListeners = new ArrayList<>();
+    // List of records for system audio mode change to handle the the caller killed in action.
+    private final ArrayList<SystemAudioModeChangeListenerRecord>
+            mSystemAudioModeChangeListenerRecords = new ArrayList<>();
+
+    // Handler used to run a task in service thread.
     private final Handler mHandler = new Handler();
 
     @Nullable
@@ -149,10 +164,13 @@
         // start to monitor the preference value and invoke SystemAudioActionFromTv if needed.
     }
 
+    @ServiceThreadOnly
     private void initializeLocalDevices(final List<Integer> deviceTypes) {
+        assertRunOnServiceThread();
         // A container for [Logical Address, Local device info].
         final SparseArray<HdmiCecLocalDevice> devices = new SparseArray<>();
         final SparseIntArray finished = new SparseIntArray();
+        mCecController.clearLogicalAddress();
         for (int type : deviceTypes) {
             final HdmiCecLocalDevice localDevice = HdmiCecLocalDevice.create(this, type);
             localDevice.init();
@@ -171,8 +189,7 @@
                     }
                     finished.append(deviceType, logicalAddress);
 
-                    // Once finish address allocation for all devices, notify
-                    // it to each device.
+                    // Address allocation completed for all devices. Notify each device.
                     if (deviceTypes.size() == finished.size()) {
                         notifyAddressAllocated(devices);
                     }
@@ -181,17 +198,21 @@
         }
     }
 
+    @ServiceThreadOnly
     private void notifyAddressAllocated(SparseArray<HdmiCecLocalDevice> devices) {
+        assertRunOnServiceThread();
         for (int i = 0; i < devices.size(); ++i) {
             int address = devices.keyAt(i);
             HdmiCecLocalDevice device = devices.valueAt(i);
-            device.onAddressAllocated(address);
+            device.handleAddressAllocated(address);
         }
     }
 
     // Initialize HDMI port information. Combine the information from CEC and MHL HAL and
     // keep them in one place.
+    @ServiceThreadOnly
     private List<HdmiPortInfo> initPortInfo() {
+        assertRunOnServiceThread();
         HdmiPortInfo[] cecPortInfo = null;
 
         // CEC HAL provides majority of the info while MHL does only MHL support flag for
@@ -311,6 +332,7 @@
         return mCecController.getVendorId();
     }
 
+    @ServiceThreadOnly
     HdmiCecDeviceInfo getDeviceInfo(int logicalAddress) {
         assertRunOnServiceThread();
         HdmiCecLocalDeviceTv tv = tv();
@@ -360,15 +382,21 @@
      * @param command CEC command to send out
      * @param callback interface used to the result of send command
      */
+    @ServiceThreadOnly
     void sendCecCommand(HdmiCecMessage command, @Nullable SendMessageCallback callback) {
+        assertRunOnServiceThread();
         mCecController.sendCommand(command, callback);
     }
 
+    @ServiceThreadOnly
     void sendCecCommand(HdmiCecMessage command) {
+        assertRunOnServiceThread();
         mCecController.sendCommand(command, null);
     }
 
+    @ServiceThreadOnly
     boolean handleCecCommand(HdmiCecMessage message) {
+        assertRunOnServiceThread();
         return dispatchMessageToLocalDevice(message);
     }
 
@@ -376,7 +404,9 @@
         mCecController.setAudioReturnChannel(enabled);
     }
 
+    @ServiceThreadOnly
     private boolean dispatchMessageToLocalDevice(HdmiCecMessage message) {
+        assertRunOnServiceThread();
         for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
             if (device.dispatchMessage(message)
                     && message.getDestination() != HdmiCec.ADDR_BROADCAST) {
@@ -394,13 +424,12 @@
      * @param portNo hdmi port number where hot plug event issued.
      * @param connected whether to be plugged in or not
      */
+    @ServiceThreadOnly
     void onHotplug(int portNo, boolean connected) {
         assertRunOnServiceThread();
-
         for (HdmiCecLocalDevice device : mCecController.getLocalDeviceList()) {
             device.onHotplug(portNo, connected);
         }
-
         announceHotplugEvent(portNo, connected);
     }
 
@@ -413,7 +442,9 @@
      * @param retryCount the number of retry used to send polling message to remote devices
      * @throw IllegalArgumentException if {@code pickStrategy} is invalid value
      */
+    @ServiceThreadOnly
     void pollDevices(DevicePollingCallback callback, int pickStrategy, int retryCount) {
+        assertRunOnServiceThread();
         mCecController.pollDevices(callback, checkPollStrategy(pickStrategy), retryCount);
     }
 
@@ -442,6 +473,12 @@
         // TODO: Hook up with AudioManager.
     }
 
+    void announceSystemAudioModeChange(boolean enabled) {
+        for (IHdmiSystemAudioModeChangeListener listener : mSystemAudioModeChangeListeners) {
+            invokeSystemAudioModeChange(listener, enabled);
+        }
+    }
+
     private HdmiCecDeviceInfo createDeviceInfo(int logicalAddress, int deviceType) {
         // TODO: find better name instead of model name.
         String displayName = Build.MODEL;
@@ -475,7 +512,7 @@
         }
 
         @Override
-            public void binderDied() {
+        public void binderDied() {
             synchronized (mLock) {
                 mDeviceEventListenerRecords.remove(this);
                 mDeviceEventListeners.remove(mListener);
@@ -483,6 +520,22 @@
         }
     }
 
+    private final class SystemAudioModeChangeListenerRecord implements IBinder.DeathRecipient {
+        private IHdmiSystemAudioModeChangeListener mListener;
+
+        public SystemAudioModeChangeListenerRecord(IHdmiSystemAudioModeChangeListener listener) {
+            mListener = listener;
+        }
+
+        @Override
+        public void binderDied() {
+            synchronized (mLock) {
+                mSystemAudioModeChangeListenerRecords.remove(this);
+                mSystemAudioModeChangeListeners.remove(mListener);
+            }
+        }
+    }
+
     private void enforceAccessPermission() {
         getContext().enforceCallingOrSelfPermission(PERMISSION, TAG);
     }
@@ -507,7 +560,7 @@
                 public void run() {
                     HdmiCecLocalDeviceTv tv = tv();
                     if (tv == null) {
-                        Slog.w(TAG, "Local playback device not available");
+                        Slog.w(TAG, "Local tv device not available");
                         invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
                         return;
                     }
@@ -517,6 +570,41 @@
         }
 
         @Override
+        public void portSelect(final int portId, final IHdmiControlCallback callback) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    HdmiCecLocalDeviceTv tv = tv();
+                    if (tv == null) {
+                        Slog.w(TAG, "Local tv device not available");
+                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
+                        return;
+                    }
+                    tv.portSelect(portId, callback);
+                }
+            });
+        }
+
+        @Override
+        public void sendKeyEvent(final int keyCode, final boolean isPressed) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    // TODO: sendKeyEvent is for TV device only for now. Allow other
+                    //       local devices of different types to use this as well.
+                    HdmiCecLocalDeviceTv tv = tv();
+                    if (tv == null) {
+                        Slog.w(TAG, "Local tv device not available");
+                        return;
+                    }
+                    tv.sendKeyEvent(keyCode, isPressed);
+                }
+            });
+        }
+
+        @Override
         public void oneTouchPlay(final IHdmiControlCallback callback) {
             enforceAccessPermission();
             runOnServiceThread(new Runnable() {
@@ -565,29 +653,71 @@
             enforceAccessPermission();
             runOnServiceThread(new Runnable() {
                 @Override
-                    public void run() {
+                public void run() {
                     HdmiControlService.this.addDeviceEventListener(listener);
                 }
             });
         }
 
         @Override
-        public void portSelect(int portId, IHdmiControlCallback callback) {
-            // TODO: Implement this
-        }
-
-        @Override
-        public void sendKeyEvent(int keyCode, boolean isPressed) {
-            // TODO: Implement this
-        }
-
-        @Override
         public List<HdmiPortInfo> getPortInfo() {
             enforceAccessPermission();
             return mPortInfo;
         }
+
+        @Override
+        public boolean canChangeSystemAudioMode() {
+            enforceAccessPermission();
+            HdmiCecLocalDeviceTv tv = tv();
+            if (tv == null) {
+                return false;
+            }
+            return tv.hasSystemAudioDevice();
+        }
+
+        @Override
+        public boolean getSystemAudioMode() {
+            enforceAccessPermission();
+            HdmiCecLocalDeviceTv tv = tv();
+            if (tv == null) {
+                return false;
+            }
+            return tv.getSystemAudioMode();
+        }
+
+        @Override
+        public void setSystemAudioMode(final boolean enabled, final IHdmiControlCallback callback) {
+            enforceAccessPermission();
+            runOnServiceThread(new Runnable() {
+                @Override
+                public void run() {
+                    HdmiCecLocalDeviceTv tv = tv();
+                    if (tv == null) {
+                        Slog.w(TAG, "Local tv device not available");
+                        invokeCallback(callback, HdmiCec.RESULT_SOURCE_NOT_AVAILABLE);
+                        return;
+                    }
+                    tv.changeSystemAudioMode(enabled, callback);
+                }
+            });
+        }
+
+        @Override
+        public void addSystemAudioModeChangeListener(
+                final IHdmiSystemAudioModeChangeListener listener) {
+            enforceAccessPermission();
+            HdmiControlService.this.addSystemAudioModeChangeListner(listener);
+        }
+
+        @Override
+        public void removeSystemAudioModeChangeListener(
+                final IHdmiSystemAudioModeChangeListener listener) {
+            enforceAccessPermission();
+            HdmiControlService.this.removeSystemAudioModeChangeListener(listener);
+        }
     }
 
+    @ServiceThreadOnly
     private void oneTouchPlay(final IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         HdmiCecLocalDevicePlayback source = playback();
@@ -599,6 +729,7 @@
         source.oneTouchPlay(callback);
     }
 
+    @ServiceThreadOnly
     private void queryDisplayStatus(final IHdmiControlCallback callback) {
         assertRunOnServiceThread();
         HdmiCecLocalDevicePlayback source = playback();
@@ -638,15 +769,57 @@
     }
 
     private void addDeviceEventListener(IHdmiDeviceEventListener listener) {
+        DeviceEventListenerRecord record = new DeviceEventListenerRecord(listener);
+        try {
+            listener.asBinder().linkToDeath(record, 0);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Listener already died");
+            return;
+        }
         synchronized (mLock) {
-            for (DeviceEventListenerRecord record : mDeviceEventListenerRecords) {
-                if (record.mListener.asBinder() == listener.asBinder()) {
+            mDeviceEventListeners.add(listener);
+            mDeviceEventListenerRecords.add(record);
+        }
+    }
+
+    void invokeDeviceEventListeners(HdmiCecDeviceInfo device, boolean activated) {
+        synchronized (mLock) {
+            for (IHdmiDeviceEventListener listener : mDeviceEventListeners) {
+                try {
+                    listener.onStatusChanged(device, activated);
+                } catch (RemoteException e) {
+                    Slog.e(TAG, "Failed to report device event:" + e);
+                }
+            }
+        }
+    }
+
+    private void addSystemAudioModeChangeListner(IHdmiSystemAudioModeChangeListener listener) {
+        SystemAudioModeChangeListenerRecord record = new SystemAudioModeChangeListenerRecord(
+                listener);
+        try {
+            listener.asBinder().linkToDeath(record, 0);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Listener already died");
+            return;
+        }
+        synchronized (mLock) {
+            mSystemAudioModeChangeListeners.add(listener);
+            mSystemAudioModeChangeListenerRecords.add(record);
+        }
+    }
+
+    private void removeSystemAudioModeChangeListener(IHdmiSystemAudioModeChangeListener listener) {
+        synchronized (mLock) {
+            for (SystemAudioModeChangeListenerRecord record :
+                    mSystemAudioModeChangeListenerRecords) {
+                if (record.mListener.asBinder() == listener) {
                     listener.asBinder().unlinkToDeath(record, 0);
-                    mDeviceEventListenerRecords.remove(record);
+                    mSystemAudioModeChangeListenerRecords.remove(record);
                     break;
                 }
             }
-            mHotplugEventListeners.remove(listener);
+            mSystemAudioModeChangeListeners.remove(listener);
         }
     }
 
@@ -658,16 +831,25 @@
         }
     }
 
-    private void announceHotplugEvent(int portNo, boolean connected) {
-        HdmiHotplugEvent event = new HdmiHotplugEvent(portNo, connected);
+    private void invokeSystemAudioModeChange(IHdmiSystemAudioModeChangeListener listener,
+            boolean enabled) {
+        try {
+            listener.onStatusChanged(enabled);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "Invoking callback failed:" + e);
+        }
+    }
+
+    private void announceHotplugEvent(int portId, boolean connected) {
+        HdmiHotplugEvent event = new HdmiHotplugEvent(portId, connected);
         synchronized (mLock) {
             for (IHdmiHotplugEventListener listener : mHotplugEventListeners) {
-                invokeHotplugEventListener(listener, event);
+                invokeHotplugEventListenerLocked(listener, event);
             }
         }
     }
 
-    private void invokeHotplugEventListener(IHdmiHotplugEventListener listener,
+    private void invokeHotplugEventListenerLocked(IHdmiHotplugEventListener listener,
             HdmiHotplugEvent event) {
         try {
             listener.onReceived(event);
diff --git a/services/core/java/com/android/server/hdmi/RoutingControlAction.java b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
index 2eec846..0d657b2 100644
--- a/services/core/java/com/android/server/hdmi/RoutingControlAction.java
+++ b/services/core/java/com/android/server/hdmi/RoutingControlAction.java
@@ -196,7 +196,7 @@
 
     // Called whenever an HDMI input of the TV shall become the active input.
     private boolean maybeChangeActiveInput(int path) {
-        if (localDevice().getActiveInput() == localDevice().pathToPortId(path)) {
+        if (localDevice().getActivePortId() == localDevice().pathToPortId(path)) {
             return false;
         }
         // TODO: Remember the currently active input
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
index e48b0dc..959a38e 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioAction.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAction.java
@@ -16,8 +16,12 @@
 
 package com.android.server.hdmi;
 
+import android.annotation.Nullable;
 import android.hardware.hdmi.HdmiCec;
 import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.RemoteException;
+import android.util.Slog;
 
 /**
  * Base feature action class for SystemAudioActionFromTv and SystemAudioActionFromAvr.
@@ -28,9 +32,6 @@
     // State in which waits for <SetSystemAudioMode>.
     private static final int STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE = 1;
 
-    // State in which waits for <ReportAudioStatus>.
-    private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 2;
-
     private static final int MAX_SEND_RETRY_COUNT = 2;
 
     private static final int ON_TIMEOUT_MS = 5000;
@@ -42,6 +43,8 @@
     // The target audio status of the action, whether to enable the system audio mode or not.
     protected boolean mTargetAudioStatus;
 
+    @Nullable private final IHdmiControlCallback mCallback;
+
     private int mSendRetryCount = 0;
 
     /**
@@ -50,13 +53,16 @@
      * @param source {@link HdmiCecLocalDevice} instance
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
+     * @param callback callback interface to be notified when it's done
      * @throw IllegalArugmentException if device type of sourceAddress and avrAddress is invalid
      */
-    SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus) {
+    SystemAudioAction(HdmiCecLocalDevice source, int avrAddress, boolean targetStatus,
+            IHdmiControlCallback callback) {
         super(source);
         HdmiUtils.verifyAddressType(avrAddress, HdmiCec.DEVICE_AUDIO_SYSTEM);
         mAvrLogicalAddress = avrAddress;
         mTargetAudioStatus = targetStatus;
+        mCallback = callback;
     }
 
     protected void sendSystemAudioModeRequest() {
@@ -72,7 +78,7 @@
                     addTimer(mState, mTargetAudioStatus ? ON_TIMEOUT_MS : OFF_TIMEOUT_MS);
                 } else {
                     setSystemAudioMode(false);
-                    finish();
+                    finishWithCallback(HdmiCec.RESULT_EXCEPTION);
                 }
             }
         });
@@ -82,7 +88,7 @@
         if (!mTargetAudioStatus  // Don't retry for Off case.
                 || mSendRetryCount++ >= MAX_SEND_RETRY_COUNT) {
             setSystemAudioMode(false);
-            finish();
+            finishWithCallback(HdmiCec.RESULT_TIMEOUT);
             return;
         }
         sendSystemAudioModeRequest();
@@ -92,39 +98,6 @@
         tv().setSystemAudioMode(mode);
     }
 
-    protected void sendGiveAudioStatus() {
-        HdmiCecMessage command = HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(),
-                mAvrLogicalAddress);
-        sendCommand(command, new HdmiControlService.SendMessageCallback() {
-            @Override
-            public void onSendCompleted(int error) {
-                if (error == HdmiConstants.SEND_RESULT_SUCCESS) {
-                    mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS;
-                    addTimer(mState, TIMEOUT_MS);
-                } else {
-                    handleSendGiveAudioStatusFailure();
-                }
-            }
-        });
-    }
-
-    private void handleSendGiveAudioStatusFailure() {
-        // TODO: Notify the failure status.
-
-        int uiCommand = tv().getSystemAudioMode()
-                ? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION  // SystemAudioMode: ON
-                : HdmiConstants.UI_COMMAND_MUTE_FUNCTION;           // SystemAudioMode: OFF
-        sendUserControlPressedAndReleased(uiCommand);
-        finish();
-    }
-
-    private void sendUserControlPressedAndReleased(int uiCommand) {
-        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
-                getSourceAddress(), mAvrLogicalAddress, uiCommand));
-        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
-                getSourceAddress(), mAvrLogicalAddress));
-    }
-
     @Override
     final boolean processCommand(HdmiCecMessage cmd) {
         switch (mState) {
@@ -137,36 +110,23 @@
                 boolean receivedStatus = HdmiUtils.parseCommandParamSystemAudioStatus(cmd);
                 if (receivedStatus == mTargetAudioStatus) {
                     setSystemAudioMode(receivedStatus);
-                    sendGiveAudioStatus();
+                    startAudioStatusAction();
+                    return true;
                 } else {
                     // Unexpected response, consider the request is newly initiated by AVR.
                     // To return 'false' will initiate new SystemAudioActionFromAvr by the control
                     // service.
-                    finish();
+                    finishWithCallback(HdmiCec.RESULT_EXCEPTION);
                     return false;
                 }
-                return true;
-
-            case STATE_WAIT_FOR_REPORT_AUDIO_STATUS:
-                // TODO: Handle <FeatureAbort> of <GiveAudioStatus>
-                if (cmd.getOpcode() != HdmiCec.MESSAGE_REPORT_AUDIO_STATUS
-                        || !HdmiUtils.checkCommandSource(cmd, mAvrLogicalAddress, TAG)) {
-                    return false;
-                }
-                byte[] params = cmd.getParams();
-                if (params.length > 0) {
-                    boolean mute = (params[0] & 0x80) == 0x80;
-                    int volume = params[0] & 0x7F;
-                    tv().setAudioStatus(mute, volume);
-                    if (mTargetAudioStatus && mute || !mTargetAudioStatus && !mute) {
-                        // Toggle AVR's mute status to match with the system audio status.
-                        sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE);
-                    }
-                }
-                finish();
-                return true;
+            default:
+                return false;
         }
-        return false;
+    }
+
+    protected void startAudioStatusAction() {
+        addAndStartAction(new SystemAudioStatusAction(tv(), mAvrLogicalAddress, mCallback));
+        finish();
     }
 
     protected void removeSystemAudioActionInProgress() {
@@ -183,9 +143,19 @@
             case STATE_WAIT_FOR_SET_SYSTEM_AUDIO_MODE:
                 handleSendSystemAudioModeRequestTimeout();
                 return;
-            case STATE_WAIT_FOR_REPORT_AUDIO_STATUS:
-                handleSendGiveAudioStatusFailure();
-                return;
         }
     }
+
+    // TODO: if IHdmiControlCallback is general to other FeatureAction,
+    //       move it into FeatureAction.
+    protected void finishWithCallback(int returnCode) {
+        if (mCallback != null) {
+            try {
+                mCallback.onComplete(returnCode);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to invoke callback.", e);
+            }
+        }
+        finish();
+    }
 }
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
index 3907b71..9d34589 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromAvr.java
@@ -17,6 +17,7 @@
 package com.android.server.hdmi;
 
 import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.IHdmiControlCallback;
 
 /**
  * Feature action that handles System Audio initiated by AVR devices.
@@ -28,11 +29,12 @@
      * @param source {@link HdmiCecLocalDevice} instance
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
+     * @param callback callback interface to be notified when it's done
      * @throw IllegalArugmentException if device type of tvAddress and avrAddress is invalid
      */
     SystemAudioActionFromAvr(HdmiCecLocalDevice source, int avrAddress,
-            boolean targetStatus) {
-        super(source, avrAddress, targetStatus);
+            boolean targetStatus, IHdmiControlCallback callback) {
+        super(source, avrAddress, targetStatus, callback);
         HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
     }
 
@@ -45,7 +47,7 @@
 
     private void handleSystemAudioActionFromAvr() {
         if (mTargetAudioStatus == tv().getSystemAudioMode()) {
-            finish();
+            finishWithCallback(HdmiCec.RESULT_SUCCESS);
             return;
         }
         if (tv().isInPresetInstallationMode()) {
@@ -59,10 +61,10 @@
         // TODO: Stop the action for System Audio Mode initialization if it is running.
         if (mTargetAudioStatus) {
             setSystemAudioMode(true);
-            sendGiveAudioStatus();
+            startAudioStatusAction();
         } else {
             setSystemAudioMode(false);
-            finish();
+            finishWithCallback(HdmiCec.RESULT_SUCCESS);
         }
     }
 }
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
index e0c4ff4..2d8f3fc 100644
--- a/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
+++ b/services/core/java/com/android/server/hdmi/SystemAudioActionFromTv.java
@@ -17,6 +17,7 @@
 package com.android.server.hdmi;
 
 import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.IHdmiControlCallback;
 
 
 /**
@@ -29,11 +30,12 @@
      * @param sourceAddress {@link HdmiCecLocalDevice} instance
      * @param avrAddress logical address of AVR device
      * @param targetStatus Whether to enable the system audio mode or not
+     * @param callback callback interface to be notified when it's done
      * @throw IllegalArugmentException if device type of tvAddress is invalid
      */
     SystemAudioActionFromTv(HdmiCecLocalDevice sourceAddress, int avrAddress,
-            boolean targetStatus) {
-        super(sourceAddress, avrAddress, targetStatus);
+            boolean targetStatus, IHdmiControlCallback callback) {
+        super(sourceAddress, avrAddress, targetStatus, callback);
         HdmiUtils.verifyAddressType(getSourceAddress(), HdmiCec.DEVICE_TV);
     }
 
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
new file mode 100644
index 0000000..845eaa9
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioAutoInitiationAction.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014 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.hdmi;
+
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
+/**
+ * Action to initiate system audio once AVR is detected on Device discovery action.
+ */
+final class SystemAudioAutoInitiationAction extends FeatureAction {
+    private final int mAvrAddress;
+
+    // State that waits for <System Audio Mode Status> once send
+    // <Give System Audio Mode Status> to AV Receiver.
+    private static final int STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS = 1;
+
+    SystemAudioAutoInitiationAction(HdmiCecLocalDevice source, int avrAddress) {
+        super(source);
+        mAvrAddress = avrAddress;
+    }
+
+    @Override
+    boolean start() {
+        mState = STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS;
+
+        addTimer(mState, TIMEOUT_MS);
+        sendGiveSystemAudioModeStatus();
+        return true;
+    }
+
+    private void sendGiveSystemAudioModeStatus() {
+        sendCommand(HdmiCecMessageBuilder.buildGiveSystemAudioModeStatus(getSourceAddress(),
+                mAvrAddress), new SendMessageCallback() {
+            @Override
+            public void onSendCompleted(int error) {
+                if (error != HdmiConstants.SEND_RESULT_SUCCESS) {
+                    tv().setSystemAudioMode(false);
+                    finish();
+                }
+            }
+        });
+    }
+
+    @Override
+    boolean processCommand(HdmiCecMessage cmd) {
+        if (mState != STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS) {
+            return false;
+        }
+
+        switch (cmd.getOpcode()) {
+            case HdmiCec.MESSAGE_SYSTEM_AUDIO_MODE_STATUS:
+                handleSystemAudioModeStatusMessage();
+                return true;
+            default:
+                return false;
+        }
+    }
+
+    private void handleSystemAudioModeStatusMessage() {
+        // If the last setting is system audio, turn on system audio whatever AVR status is.
+        if (tv().getSystemAudioMode()) {
+            if (canChangeSystemAudio()) {
+                addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true, null));
+            }
+        } else {
+            // If the last setting is non-system audio, turn off system audio mode
+            // and update system audio status (volume or mute).
+            tv().setSystemAudioMode(false);
+            if (canChangeSystemAudio()) {
+                addAndStartAction(new SystemAudioStatusAction(tv(), mAvrAddress, null));
+            }
+        }
+        finish();
+    }
+
+    @Override
+    void handleTimerEvent(int state) {
+        if (mState != state) {
+            return;
+        }
+
+        switch (mState) {
+            case STATE_WAITING_FOR_SYSTEM_AUDIO_MODE_STATUS:
+                handleSystemAudioModeStatusTimeout();
+                break;
+        }
+    }
+
+    private void handleSystemAudioModeStatusTimeout() {
+        if (tv().getSystemAudioMode()) {
+            if (canChangeSystemAudio()) {
+                addAndStartAction(new SystemAudioActionFromTv(tv(), mAvrAddress, true, null));
+            }
+        } else {
+            tv().setSystemAudioMode(false);
+        }
+        finish();
+    }
+
+    private boolean canChangeSystemAudio() {
+        return tv().canChangeSystemAudio();
+    }
+}
diff --git a/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
new file mode 100644
index 0000000..89206a7
--- /dev/null
+++ b/services/core/java/com/android/server/hdmi/SystemAudioStatusAction.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2014 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.hdmi;
+
+import android.annotation.Nullable;
+import android.hardware.hdmi.HdmiCec;
+import android.hardware.hdmi.HdmiCecMessage;
+import android.hardware.hdmi.IHdmiControlCallback;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.server.hdmi.HdmiControlService.SendMessageCallback;
+
+/**
+ * Action to update audio status (volume or mute) of audio amplifier
+ */
+final class SystemAudioStatusAction extends FeatureAction {
+    private static final String TAG = "SystemAudioStatusAction";
+
+    // State that waits for <ReportAudioStatus>.
+    private static final int STATE_WAIT_FOR_REPORT_AUDIO_STATUS = 1;
+
+    private final int mAvrAddress;
+    @Nullable private final IHdmiControlCallback mCallback;
+
+    SystemAudioStatusAction(HdmiCecLocalDevice source, int avrAddress,
+            IHdmiControlCallback callback) {
+        super(source);
+        mAvrAddress = avrAddress;
+        mCallback = callback;
+    }
+
+    @Override
+    boolean start() {
+        mState = STATE_WAIT_FOR_REPORT_AUDIO_STATUS;
+        addTimer(mState, TIMEOUT_MS);
+        sendGiveAudioStatus();
+        return true;
+    }
+
+    private void sendGiveAudioStatus() {
+        sendCommand(HdmiCecMessageBuilder.buildGiveAudioStatus(getSourceAddress(), mAvrAddress),
+                new SendMessageCallback() {
+            @Override
+            public void onSendCompleted(int error) {
+                if (error != HdmiConstants.SEND_RESULT_SUCCESS) {
+                    handleSendGiveAudioStatusFailure();
+                }
+            }
+        });
+    }
+
+    private void handleSendGiveAudioStatusFailure() {
+        // Inform to all application that the audio status (volumn, mute) of
+        // the audio amplifier is unknown.
+        tv().setAudioStatus(false, HdmiConstants.UNKNOWN_VOLUME);
+
+        int uiCommand = tv().getSystemAudioMode()
+                ? HdmiConstants.UI_COMMAND_RESTORE_VOLUME_FUNCTION  // SystemAudioMode: ON
+                : HdmiConstants.UI_COMMAND_MUTE_FUNCTION;           // SystemAudioMode: OFF
+        sendUserControlPressedAndReleased(uiCommand);
+
+        // Still return SUCCESS to callback.
+        finishWithCallback(HdmiCec.RESULT_SUCCESS);
+    }
+
+    private void sendUserControlPressedAndReleased(int uiCommand) {
+        sendCommand(HdmiCecMessageBuilder.buildUserControlPressed(
+                getSourceAddress(), mAvrAddress, uiCommand));
+        sendCommand(HdmiCecMessageBuilder.buildUserControlReleased(
+                getSourceAddress(), mAvrAddress));
+    }
+
+    @Override
+    boolean processCommand(HdmiCecMessage cmd) {
+        if (mState != STATE_WAIT_FOR_REPORT_AUDIO_STATUS) {
+            return false;
+        }
+
+        switch (cmd.getOpcode()) {
+            case HdmiCec.MESSAGE_REPORT_AUDIO_STATUS:
+                handleReportAudioStatus(cmd);
+                return true;
+        }
+
+        return false;
+    }
+
+    private void handleReportAudioStatus(HdmiCecMessage cmd) {
+        byte[] params = cmd.getParams();
+        if (params.length > 0) {
+            boolean mute = (params[0] & 0x80) == 0x80;
+            int volume = params[0] & 0x7F;
+            tv().setAudioStatus(mute, volume);
+
+            if ((tv().getSystemAudioMode() && mute) || (!tv().getSystemAudioMode() && !mute)) {
+                // Toggle AVR's mute status to match with the system audio status.
+                sendUserControlPressedAndReleased(HdmiConstants.UI_COMMAND_MUTE);
+            }
+            finishWithCallback(HdmiCec.RESULT_SUCCESS);
+        } else {
+            Slog.e(TAG, "Invalid <Report Audio Status> message:" + cmd);
+            handleSendGiveAudioStatusFailure();
+            return;
+        }
+    }
+
+    private void finishWithCallback(int returnCode) {
+        if (mCallback != null) {
+            try {
+                mCallback.onComplete(returnCode);
+            } catch (RemoteException e) {
+                Slog.e(TAG, "Failed to invoke callback.", e);
+            }
+        }
+        finish();
+    }
+
+    @Override
+    void handleTimerEvent(int state) {
+        if (mState != state) {
+            return;
+        }
+
+        handleSendGiveAudioStatusFailure();
+    }
+}
diff --git a/services/core/java/com/android/server/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index 0e9a9cc..cab2728 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -22,6 +22,7 @@
 import java.util.Iterator;
 import java.util.List;
 
+import android.app.AppGlobals;
 import android.app.job.JobInfo;
 import android.app.job.JobScheduler;
 import android.app.job.JobService;
@@ -31,8 +32,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.IPackageManager;
 import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.content.pm.ServiceInfo;
 import android.os.Binder;
 import android.os.Handler;
@@ -616,10 +617,13 @@
         // job that runs one of the app's services, as well as verifying that the
         // named service properly requires the BIND_JOB_SERVICE permission
         private void enforceValidJobRequest(int uid, JobInfo job) {
-            final PackageManager pm = getContext().getPackageManager();
+            final IPackageManager pm = AppGlobals.getPackageManager();
             final ComponentName service = job.getService();
             try {
-                ServiceInfo si = pm.getServiceInfo(service, 0);
+                ServiceInfo si = pm.getServiceInfo(service, 0, UserHandle.getUserId(uid));
+                if (si == null) {
+                    throw new IllegalArgumentException("No such service " + service);
+                }
                 if (si.applicationInfo.uid != uid) {
                     throw new IllegalArgumentException("uid " + uid +
                             " cannot schedule job in " + service.getPackageName());
@@ -628,8 +632,8 @@
                     throw new IllegalArgumentException("Scheduled service " + service
                             + " does not require android.permission.BIND_JOB_SERVICE permission");
                 }
-            } catch (NameNotFoundException e) {
-                throw new IllegalArgumentException("No such service: " + service);
+            } catch (RemoteException e) {
+                // Can't happen; the Package Manager is in this same process
             }
         }
 
diff --git a/services/core/java/com/android/server/job/controllers/BatteryController.java b/services/core/java/com/android/server/job/controllers/BatteryController.java
index 4aef2d31..538a252 100644
--- a/services/core/java/com/android/server/job/controllers/BatteryController.java
+++ b/services/core/java/com/android/server/job/controllers/BatteryController.java
@@ -19,19 +19,16 @@
 import android.app.AlarmManager;
 import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
-import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
 import android.os.BatteryManager;
-import android.os.BatteryProperty;
-import android.os.RemoteException;
-import android.os.ServiceManager;
+import android.os.BatteryManagerInternal;
 import android.os.SystemClock;
 import android.util.Slog;
 
 import com.android.internal.annotations.VisibleForTesting;
-import com.android.server.BatteryService;
+import com.android.server.LocalServices;
 import com.android.server.job.JobSchedulerService;
 import com.android.server.job.StateChangedListener;
 
@@ -158,14 +155,10 @@
             mContext.registerReceiver(this, filter);
 
             // Initialise tracker state.
-            BatteryService batteryService = (BatteryService) ServiceManager.getService("battery");
-            if (batteryService != null) {
-                mBatteryHealthy = !batteryService.getBatteryLevelLow();
-                mCharging = batteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
-            } else {
-                // Unavailable for some reason, we default to false and let ACTION_BATTERY_[OK,LOW]
-                // sort it out.
-            }
+            BatteryManagerInternal batteryManagerInternal =
+                    LocalServices.getService(BatteryManagerInternal.class);
+            mBatteryHealthy = !batteryManagerInternal.getBatteryLevelLow();
+            mCharging = batteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
         }
 
         boolean isOnStablePower() {
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index 15a6b25..53337c4 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -203,7 +203,8 @@
     @Override
     public String toString() {
         return String.valueOf(hashCode()).substring(0, 3) + ".."
-                + ":[" + job.getService().getPackageName() + ",jId=" + job.getId()
+                + ":[" + job.getService()
+                + ",jId=" + job.getId()
                 + ",R=(" + earliestRunTimeElapsedMillis + "," + latestRunTimeElapsedMillis + ")"
                 + ",N=" + job.getNetworkCapabilities() + ",C=" + job.isRequireCharging()
                 + ",I=" + job.isRequireDeviceIdle() + ",F=" + numFailures
diff --git a/services/core/java/com/android/server/media/MediaSessionRecord.java b/services/core/java/com/android/server/media/MediaSessionRecord.java
index 9ae8aed..1264741 100644
--- a/services/core/java/com/android/server/media/MediaSessionRecord.java
+++ b/services/core/java/com/android/server/media/MediaSessionRecord.java
@@ -17,6 +17,8 @@
 package com.android.server.media;
 
 import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.media.routeprovider.RouteRequest;
@@ -25,7 +27,6 @@
 import android.media.session.ISession;
 import android.media.session.ISessionCallback;
 import android.media.session.MediaController;
-import android.media.session.RemoteVolumeProvider;
 import android.media.session.RouteCommand;
 import android.media.session.RouteInfo;
 import android.media.session.RouteOptions;
@@ -34,10 +35,13 @@
 import android.media.session.MediaSessionInfo;
 import android.media.session.RouteInterface;
 import android.media.session.PlaybackState;
+import android.media.session.ParcelableVolumeInfo;
 import android.media.AudioManager;
 import android.media.MediaMetadata;
 import android.media.Rating;
+import android.media.VolumeProvider;
 import android.os.Bundle;
+import android.os.DeadObjectException;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.Looper;
@@ -63,6 +67,7 @@
  */
 public class MediaSessionRecord implements IBinder.DeathRecipient {
     private static final String TAG = "MediaSessionRecord";
+    private static final boolean DEBUG = false;
 
     /**
      * These are the playback states that count as currently active.
@@ -105,6 +110,7 @@
     // TODO define a RouteState class with relevant info
     private int mRouteState;
     private long mFlags;
+    private ComponentName mMediaButtonReceiver;
 
     // TransportPerformer fields
 
@@ -115,9 +121,10 @@
     // End TransportPerformer fields
 
     // Volume handling fields
-    private int mPlaybackType = MediaSession.VOLUME_TYPE_LOCAL;
+    private AudioManager mAudioManager;
+    private int mVolumeType = MediaSession.VOLUME_TYPE_LOCAL;
     private int mAudioStream = AudioManager.STREAM_MUSIC;
-    private int mVolumeControlType = RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+    private int mVolumeControlType = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
     private int mMaxVolume = 0;
     private int mCurrentVolume = 0;
     // End volume handling fields
@@ -138,6 +145,7 @@
         mSessionCb = new SessionCb(cb);
         mService = service;
         mHandler = new MessageHandler(handler.getLooper());
+        mAudioManager = (AudioManager) service.getContext().getSystemService(Context.AUDIO_SERVICE);
     }
 
     /**
@@ -185,6 +193,10 @@
         return mSessionInfo;
     }
 
+    public ComponentName getMediaButtonReceiver() {
+        return mMediaButtonReceiver;
+    }
+
     /**
      * Get this session's flags.
      *
@@ -263,20 +275,42 @@
      *
      * @param delta The amount to adjust the volume by.
      */
-    public void adjustVolumeBy(int delta) {
-        if (mVolumeControlType == RemoteVolumeProvider.VOLUME_CONTROL_FIXED) {
-            // Nothing to do, the volume cannot be changed
-            return;
+    public void adjustVolumeBy(int delta, int flags) {
+        if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) {
+            if (delta == 0) {
+                mAudioManager.adjustStreamVolume(mAudioStream, delta, flags);
+            } else {
+                int direction = 0;
+                int steps = delta;
+                if (delta > 0) {
+                    direction = 1;
+                } else if (delta < 0) {
+                    direction = -1;
+                    steps = -delta;
+                }
+                for (int i = 0; i < steps; i++) {
+                    mAudioManager.adjustStreamVolume(mAudioStream, direction, flags);
+                }
+            }
+        } else {
+            if (mVolumeControlType == VolumeProvider.VOLUME_CONTROL_FIXED) {
+                // Nothing to do, the volume cannot be changed
+                return;
+            }
+            mSessionCb.adjustVolumeBy(delta);
         }
-        mSessionCb.adjustVolumeBy(delta);
     }
 
-    public void setVolumeTo(int value) {
-        if (mVolumeControlType != RemoteVolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
-            // Nothing to do. The volume can't be set directly.
-            return;
+    public void setVolumeTo(int value, int flags) {
+        if (mVolumeType == MediaSession.VOLUME_TYPE_LOCAL) {
+            mAudioManager.setStreamVolume(mAudioStream, value, flags);
+        } else {
+            if (mVolumeControlType != VolumeProvider.VOLUME_CONTROL_ABSOLUTE) {
+                // Nothing to do. The volume can't be set directly.
+                return;
+            }
+            mSessionCb.setVolumeTo(value);
         }
-        mSessionCb.setVolumeTo(value);
     }
 
     /**
@@ -350,7 +384,7 @@
      * @return The current type of playback.
      */
     public int getPlaybackType() {
-        return mPlaybackType;
+        return mVolumeType;
     }
 
     /**
@@ -506,9 +540,12 @@
                 ISessionControllerCallback cb = mControllerCallbacks.get(i);
                 try {
                     cb.onPlaybackStateChanged(mPlaybackState);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Removing dead callback in pushPlaybackStateUpdate.", e);
+                } catch (DeadObjectException e) {
                     mControllerCallbacks.remove(i);
+                    Log.w(TAG, "Removed dead callback in pushPlaybackStateUpdate. size="
+                            + mControllerCallbacks.size() + " cb=" + cb, e);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "unexpected exception in pushPlaybackStateUpdate.", e);
                 }
             }
         }
@@ -523,9 +560,11 @@
                 ISessionControllerCallback cb = mControllerCallbacks.get(i);
                 try {
                     cb.onMetadataChanged(mMetadata);
-                } catch (RemoteException e) {
-                    Log.w(TAG, "Removing dead callback in pushMetadataUpdate.", e);
+                } catch (DeadObjectException e) {
+                    Log.w(TAG, "Removing dead callback in pushMetadataUpdate. " + cb, e);
                     mControllerCallbacks.remove(i);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "unexpected exception in pushMetadataUpdate. " + cb, e);
                 }
             }
         }
@@ -540,9 +579,11 @@
                 ISessionControllerCallback cb = mControllerCallbacks.get(i);
                 try {
                     cb.onRouteChanged(mRoute);
-                } catch (RemoteException e) {
+                } catch (DeadObjectException e) {
                     Log.w(TAG, "Removing dead callback in pushRouteUpdate.", e);
                     mControllerCallbacks.remove(i);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "unexpected exception in pushRouteUpdate.", e);
                 }
             }
         }
@@ -557,8 +598,11 @@
                 ISessionControllerCallback cb = mControllerCallbacks.get(i);
                 try {
                     cb.onEvent(event, data);
+                } catch (DeadObjectException e) {
+                    Log.w(TAG, "Removing dead callback in pushEvent.", e);
+                    mControllerCallbacks.remove(i);
                 } catch (RemoteException e) {
-                    Log.w(TAG, "Error with callback in pushEvent.", e);
+                    Log.w(TAG, "unexpected exception in pushEvent.", e);
                 }
             }
         }
@@ -611,6 +655,16 @@
         return result == null ? state : result;
     }
 
+    private int getControllerCbIndexForCb(ISessionControllerCallback cb) {
+        IBinder binder = cb.asBinder();
+        for (int i = mControllerCallbacks.size() - 1; i >= 0; i--) {
+            if (binder.equals(mControllerCallbacks.get(i).asBinder())) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
     private final RouteConnectionRecord.Listener mConnectionListener
             = new RouteConnectionRecord.Listener() {
         @Override
@@ -661,6 +715,11 @@
         }
 
         @Override
+        public void setMediaButtonReceiver(ComponentName mbr) {
+            mMediaButtonReceiver = mbr;
+        }
+
+        @Override
         public void setMetadata(MediaMetadata metadata) {
             mMetadata = metadata;
             mHandler.post(MessageHandler.MSG_UPDATE_METADATA);
@@ -732,7 +791,7 @@
         public void configureVolumeHandling(int type, int arg1, int arg2) throws RemoteException {
             switch(type) {
                 case MediaSession.VOLUME_TYPE_LOCAL:
-                    mPlaybackType = type;
+                    mVolumeType = type;
                     int audioStream = arg1;
                     if (isValidStream(audioStream)) {
                         mAudioStream = audioStream;
@@ -742,7 +801,7 @@
                     }
                     break;
                 case MediaSession.VOLUME_TYPE_REMOTE:
-                    mPlaybackType = type;
+                    mVolumeType = type;
                     mVolumeControlType = arg1;
                     mMaxVolume = arg2;
                     break;
@@ -929,8 +988,11 @@
         @Override
         public void registerCallbackListener(ISessionControllerCallback cb) {
             synchronized (mLock) {
-                if (!mControllerCallbacks.contains(cb)) {
+                if (getControllerCbIndexForCb(cb) < 0) {
                     mControllerCallbacks.add(cb);
+                    if (DEBUG) {
+                        Log.d(TAG, "registering controller callback " + cb);
+                    }
                 }
             }
         }
@@ -939,7 +1001,13 @@
         public void unregisterCallbackListener(ISessionControllerCallback cb)
                 throws RemoteException {
             synchronized (mLock) {
-                mControllerCallbacks.remove(cb);
+                int index = getControllerCbIndexForCb(cb);
+                if (index != -1) {
+                    mControllerCallbacks.remove(index);
+                }
+                if (DEBUG) {
+                    Log.d(TAG, "unregistering callback " + cb + ". index=" + index);
+                }
             }
         }
 
@@ -954,6 +1022,35 @@
         }
 
         @Override
+        public ParcelableVolumeInfo getVolumeAttributes() {
+            synchronized (mLock) {
+                int type;
+                int max;
+                int current;
+                if (mVolumeType == MediaSession.VOLUME_TYPE_REMOTE) {
+                    type = mVolumeControlType;
+                    max = mMaxVolume;
+                    current = mCurrentVolume;
+                } else {
+                    type = VolumeProvider.VOLUME_CONTROL_ABSOLUTE;
+                    max = mAudioManager.getStreamMaxVolume(mAudioStream);
+                    current = mAudioManager.getStreamVolume(mAudioStream);
+                }
+                return new ParcelableVolumeInfo(mVolumeType, mAudioStream, type, max, current);
+            }
+        }
+
+        @Override
+        public void adjustVolumeBy(int delta, int flags) {
+            MediaSessionRecord.this.adjustVolumeBy(delta, flags);
+        }
+
+        @Override
+        public void setVolumeTo(int value, int flags) {
+            MediaSessionRecord.this.setVolumeTo(value, flags);
+        }
+
+        @Override
         public void play() throws RemoteException {
             mSessionCb.play();
         }
diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java
index 67065ba..c0b7d68 100644
--- a/services/core/java/com/android/server/media/MediaSessionService.java
+++ b/services/core/java/com/android/server/media/MediaSessionService.java
@@ -23,6 +23,7 @@
 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.pm.PackageManager;
@@ -88,6 +89,7 @@
 
     private KeyguardManager mKeyguardManager;
     private IAudioService mAudioService;
+    private ContentResolver mContentResolver;
 
     private MediaSessionRecord mPrioritySession;
     private int mCurrentUserId = -1;
@@ -115,6 +117,7 @@
         mKeyguardManager =
                 (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
         mAudioService = getAudioService();
+        mContentResolver = getContext().getContentResolver();
     }
 
     private IAudioService getAudioService() {
@@ -380,9 +383,11 @@
             // You may not access another user's content as an enabled listener.
             return false;
         }
+        if (DEBUG) {
+            Log.d(TAG, "Checking if enabled notification listener " + compName);
+        }
         if (compName != null) {
-            final String enabledNotifListeners = Settings.Secure.getStringForUser(
-                    getContext().getContentResolver(),
+            final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
                     Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
                     userId);
             if (enabledNotifListeners != null) {
@@ -485,6 +490,9 @@
         synchronized (mLock) {
             List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
             int size = records.size();
+            if (size > 0) {
+                persistMediaButtonReceiverLocked(records.get(0));
+            }
             ArrayList<MediaSessionToken> tokens = new ArrayList<MediaSessionToken>();
             for (int i = 0; i < size; i++) {
                 tokens.add(new MediaSessionToken(records.get(i).getControllerBinder()));
@@ -504,6 +512,16 @@
         }
     }
 
+    private void persistMediaButtonReceiverLocked(MediaSessionRecord record) {
+        ComponentName receiver = record.getMediaButtonReceiver();
+        if (receiver != null) {
+            Settings.System.putStringForUser(mContentResolver,
+                    Settings.System.MEDIA_BUTTON_RECEIVER,
+                    receiver == null ? "" : receiver.flattenToString(),
+                    UserHandle.USER_CURRENT);
+        }
+    }
+
     private MediaRouteProviderProxy.RoutesListener mRoutesCallback
             = new MediaRouteProviderProxy.RoutesListener() {
         @Override
@@ -881,14 +899,6 @@
 
         private void dispatchAdjustVolumeByLocked(int suggestedStream, int delta, int flags,
                 MediaSessionRecord session) {
-            int direction = 0;
-            int steps = delta;
-            if (delta > 0) {
-                direction = 1;
-            } else if (delta < 0) {
-                direction = -1;
-                steps = -delta;
-            }
             if (DEBUG) {
                 String sessionInfo = session == null ? null : session.getSessionInfo().toString();
                 Log.d(TAG, "Adjusting session " + sessionInfo + " by " + delta + ". flags=" + flags
@@ -901,6 +911,14 @@
                         mAudioService.adjustSuggestedStreamVolume(delta, suggestedStream, flags,
                                 getContext().getOpPackageName());
                     } else {
+                        int direction = 0;
+                        int steps = delta;
+                        if (delta > 0) {
+                            direction = 1;
+                        } else if (delta < 0) {
+                            direction = -1;
+                            steps = -delta;
+                        }
                         for (int i = 0; i < steps; i++) {
                             mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
                                     flags, getContext().getOpPackageName());
@@ -910,26 +928,7 @@
                     Log.e(TAG, "Error adjusting default volume.", e);
                 }
             } else {
-                if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_LOCAL) {
-                    try {
-                        if (delta == 0) {
-                            mAudioService.adjustSuggestedStreamVolume(delta,
-                                    session.getAudioStream(), flags,
-                                    getContext().getOpPackageName());
-                        } else {
-                            for (int i = 0; i < steps; i++) {
-                                mAudioService.adjustSuggestedStreamVolume(direction,
-                                        session.getAudioStream(), flags,
-                                        getContext().getOpPackageName());
-                            }
-                        }
-                    } catch (RemoteException e) {
-                        Log.e(TAG, "Error adjusting volume for stream "
-                                + session.getAudioStream(), e);
-                    }
-                } else if (session.getPlaybackType() == MediaSession.VOLUME_TYPE_REMOTE) {
-                    session.adjustVolumeBy(delta);
-                }
+                session.adjustVolumeBy(delta, flags);
             }
         }
 
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 825dc84..c7adb68 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -1546,41 +1546,11 @@
 
                     Collections.sort(mNotificationList, mRankingComparator);
 
-                    final int currentUser;
-                    final long token = Binder.clearCallingIdentity();
-                    try {
-                        currentUser = ActivityManager.getCurrentUser();
-                    } finally {
-                        Binder.restoreCallingIdentity(token);
-                    }
-
                     if (notification.icon != 0) {
-                        if (old != null && !old.isCanceled) {
-                            final long identity = Binder.clearCallingIdentity();
-                            try {
-                                mStatusBar.updateNotification(n);
-                            } finally {
-                                Binder.restoreCallingIdentity(identity);
-                            }
-                        } else {
-                            final long identity = Binder.clearCallingIdentity();
-                            try {
-                                mStatusBar.addNotification(n);
-                            } finally {
-                                Binder.restoreCallingIdentity(identity);
-                            }
-                        }
                         mListeners.notifyPostedLocked(n);
                     } else {
                         Slog.e(TAG, "Not posting notification with icon==0: " + notification);
                         if (old != null && !old.isCanceled) {
-                            final long identity = Binder.clearCallingIdentity();
-                            try {
-                                mStatusBar.removeNotification(r.getKey());
-                            } finally {
-                                Binder.restoreCallingIdentity(identity);
-                            }
-
                             mListeners.notifyRemovedLocked(n);
                         }
                         // ATTENTION: in a future release we will bail out here
@@ -1992,12 +1962,6 @@
 
         // status bar
         if (r.getNotification().icon != 0) {
-            final long identity = Binder.clearCallingIdentity();
-            try {
-                mStatusBar.removeNotification(r.getKey());
-            } finally {
-                Binder.restoreCallingIdentity(identity);
-            }
             r.isCanceled = true;
             mListeners.notifyRemovedLocked(r.sbn);
         }
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index f2db791..1d53016 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,12 +16,13 @@
 
 package com.android.server.pm;
 
-import android.content.BroadcastReceiver;
+import android.app.job.JobInfo;
+import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
+import android.app.job.JobService;
+import android.content.ComponentName;
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.os.ServiceManager;
-import android.os.UserHandle;
 import android.util.Log;
 
 import java.util.HashSet;
@@ -30,62 +31,63 @@
 /**
  * {@hide}
  */
-public class BackgroundDexOptService {
-
+public class BackgroundDexOptService extends JobService {
     static final String TAG = "BackgroundDexOptService";
 
-    private final BroadcastReceiver mIdleMaintenanceReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
-            if (Intent.ACTION_IDLE_MAINTENANCE_START.equals(action)) {
-                onIdleStart();
-            } else if (Intent.ACTION_IDLE_MAINTENANCE_END.equals(action)) {
-                onIdleStop();
-            }
-        }
-    };
-
-    final PackageManagerService mPackageManager;
+    static final int BACKGROUND_DEXOPT_JOB = 800;
+    private static ComponentName sDexoptServiceName = new ComponentName(
+            "android",
+            BackgroundDexOptService.class.getName());
 
     final AtomicBoolean mIdleTime = new AtomicBoolean(false);
 
-    public BackgroundDexOptService(Context context) {
-        mPackageManager = (PackageManagerService)ServiceManager.getService("package");
-
-        IntentFilter idleMaintenanceFilter = new IntentFilter();
-        idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_START);
-        idleMaintenanceFilter.addAction(Intent.ACTION_IDLE_MAINTENANCE_END);
-        context.registerReceiverAsUser(mIdleMaintenanceReceiver, UserHandle.ALL,
-                                       idleMaintenanceFilter, null, null);
+    public static void schedule(Context context) {
+        JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        JobInfo job = new JobInfo.Builder(BACKGROUND_DEXOPT_JOB, sDexoptServiceName)
+                .setRequiresDeviceIdle(true)
+                .setRequiresCharging(true)
+                .build();
+        js.schedule(job);
     }
 
-    public boolean onIdleStart() {
+    @Override
+    public boolean onStartJob(JobParameters params) {
         Log.i(TAG, "onIdleStart");
-        if (mPackageManager.isStorageLow()) {
+        final PackageManagerService pm =
+                (PackageManagerService)ServiceManager.getService("package");
+
+        if (pm.isStorageLow()) {
             return false;
         }
-        final HashSet<String> pkgs = mPackageManager.getPackagesThatNeedDexOpt();
+        final HashSet<String> pkgs = pm.getPackagesThatNeedDexOpt();
         if (pkgs == null) {
             return false;
         }
+
+        final JobParameters jobParams = params;
         mIdleTime.set(true);
         new Thread("BackgroundDexOptService_DexOpter") {
             @Override
             public void run() {
                 for (String pkg : pkgs) {
                     if (!mIdleTime.get()) {
-                        break;
+                        // stopped while still working, so we need to reschedule
+                        schedule(BackgroundDexOptService.this);
+                        return;
                     }
-                    mPackageManager.performDexOpt(pkg, false);
+                    pm.performDexOpt(pkg, false);
                 }
+                // ran to completion, so we abandon our timeslice and do not reschedule
+                jobFinished(jobParams, false);
             }
         }.start();
         return true;
     }
 
-    public void onIdleStop() {
+    @Override
+    public boolean onStopJob(JobParameters params) {
         Log.i(TAG, "onIdleStop");
         mIdleTime.set(false);
+        return false;
     }
 }
diff --git a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
index 3d432dc..3ce19c1f 100644
--- a/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
+++ b/services/core/java/com/android/server/pm/CrossProfileIntentFilter.java
@@ -32,36 +32,32 @@
  */
 class CrossProfileIntentFilter extends IntentFilter {
     private static final String ATTR_TARGET_USER_ID = "targetUserId";
-    private static final String ATTR_USER_ID_DEST = "userIdDest";//Old name. Kept for compatibility.
-    private static final String ATTR_REMOVABLE = "removable";
+    private static final String ATTR_FLAGS = "flags";
     private static final String ATTR_FILTER = "filter";
 
     private static final String TAG = "CrossProfileIntentFilter";
 
     // If the intent matches the IntentFilter, then it can be forwarded to this userId.
     final int mTargetUserId;
-    boolean mRemovable;
+    final int mFlags;
 
-    CrossProfileIntentFilter(IntentFilter filter, boolean removable, int targetUserId) {
+    CrossProfileIntentFilter(IntentFilter filter, int targetUserId, int flags) {
         super(filter);
         mTargetUserId = targetUserId;
-        mRemovable = removable;
+        mFlags = flags;
     }
 
     public int getTargetUserId() {
         return mTargetUserId;
     }
 
-    public boolean isRemovable() {
-        return mRemovable;
+    public int getFlags() {
+        return mFlags;
     }
 
     CrossProfileIntentFilter(XmlPullParser parser) throws XmlPullParserException, IOException {
         String targetUserIdString = parser.getAttributeValue(null, ATTR_TARGET_USER_ID);
         if (targetUserIdString == null) {
-            targetUserIdString = parser.getAttributeValue(null, ATTR_USER_ID_DEST);
-        }
-        if (targetUserIdString == null) {
             String msg = "Missing element under " + TAG +": " + ATTR_TARGET_USER_ID + " at " +
                     parser.getPositionDescription();
             PackageManagerService.reportSettingsProblem(Log.WARN, msg);
@@ -69,9 +65,14 @@
         } else {
             mTargetUserId = Integer.parseInt(targetUserIdString);
         }
-        String removableString = parser.getAttributeValue(null, ATTR_REMOVABLE);
-        if (removableString != null) {
-            mRemovable = Boolean.parseBoolean(removableString);
+        String flagsString = parser.getAttributeValue(null, ATTR_FLAGS);
+        if (flagsString == null) {
+            String msg = "Missing element under " + TAG +": " + ATTR_FLAGS + " at " +
+                    parser.getPositionDescription();
+            PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+            mFlags = 0;
+        } else {
+            mFlags = Integer.parseInt(flagsString);
         }
         int outerDepth = parser.getDepth();
         String tagName = parser.getName();
@@ -104,7 +105,7 @@
 
     public void writeToXml(XmlSerializer serializer) throws IOException {
         serializer.attribute(null, ATTR_TARGET_USER_ID, Integer.toString(mTargetUserId));
-        serializer.attribute(null, ATTR_REMOVABLE, Boolean.toString(mRemovable));
+        serializer.attribute(null, ATTR_FLAGS, Integer.toString(mFlags));
         serializer.startTag(null, ATTR_FILTER);
             super.writeToXml(serializer);
         serializer.endTag(null, ATTR_FILTER);
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index c0f1eec..7189960 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -27,6 +27,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.util.List;
 
 public final class Installer extends SystemService {
     private static final String TAG = "Installer";
@@ -345,8 +346,8 @@
         }
     }
 
-    public int pruneDexCache() {
-        return execute("prunedexcache");
+    public int pruneDexCache(String cacheSubDir) {
+        return execute("prunedexcache " + cacheSubDir);
     }
 
     public int freeCache(long freeStorageSize) {
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index dd33771..89ab2ae 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -29,6 +29,7 @@
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser;
 import android.content.pm.PackageParser.ApkLite;
+import android.content.pm.PackageParser.PackageParserException;
 import android.content.pm.Signature;
 import android.os.Build;
 import android.os.Bundle;
@@ -50,14 +51,10 @@
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.Preconditions;
 
-import libcore.io.IoUtils;
 import libcore.io.Libcore;
-import libcore.io.Streams;
 
 import java.io.File;
 import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.ArrayList;
 
@@ -297,11 +294,12 @@
 
         // Verify that all staged packages are internally consistent
         for (File file : files) {
-            final ApkLite info = PackageParser.parseApkLite(file.getAbsolutePath(),
-                    PackageParser.PARSE_GET_SIGNATURES);
-            if (info == null) {
+            final ApkLite info;
+            try {
+                info = PackageParser.parseApkLite(file, PackageParser.PARSE_GET_SIGNATURES);
+            } catch (PackageParserException e) {
                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
-                        "Failed to parse " + file);
+                        "Failed to parse " + file + ": " + e);
             }
 
             if (!seenSplits.add(info.splitName)) {
@@ -356,11 +354,13 @@
                         "Missing existing base package for " + mPackageName);
             }
 
-            final ApkLite info = PackageParser.parseApkLite(app.sourceDir,
-                    PackageParser.PARSE_GET_SIGNATURES);
-            if (info == null) {
+            final ApkLite info;
+            try {
+                info = PackageParser.parseApkLite(new File(app.sourceDir),
+                        PackageParser.PARSE_GET_SIGNATURES);
+            } catch (PackageParserException e) {
                 throw new InstallFailedException(INSTALL_FAILED_INVALID_APK,
-                        "Failed to parse existing base " + app.sourceDir);
+                        "Failed to parse existing base " + app.sourceDir + ": " + e);
             }
 
             assertPackageConsistent("Existing base", info.packageName, info.versionCode,
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 8d94396..7fc7d0d 100755
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -42,6 +42,7 @@
 import com.android.internal.content.NativeLibraryHelper;
 import com.android.internal.content.NativeLibraryHelper.ApkHandle;
 import com.android.internal.content.PackageHelper;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.FastPrintWriter;
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.XmlUtils;
@@ -52,7 +53,6 @@
 import com.android.server.Watchdog;
 import com.android.server.pm.Settings.DatabaseVersion;
 import com.android.server.storage.DeviceStorageMonitorInternal;
-import com.android.server.storage.DeviceStorageMonitorInternal;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -90,7 +90,6 @@
 import android.content.pm.PackageCleanItem;
 import android.content.pm.PackageInfo;
 import android.content.pm.PackageInfoLite;
-import android.content.pm.PackageInstallerParams;
 import android.content.pm.PackageManager;
 import android.content.pm.PackageParser.ActivityIntentInfo;
 import android.content.pm.PackageParser.PackageParserException;
@@ -146,6 +145,7 @@
 import android.util.PrintStreamPrinter;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.SparseBooleanArray;
 import android.util.Xml;
 import android.view.Display;
 
@@ -621,10 +621,10 @@
         private final AtomicLong mLastWritten = new AtomicLong(0);
         private final AtomicBoolean mBackgroundWriteRunning = new AtomicBoolean(false);
 
-        private boolean mIsFirstBoot = false;
+        private boolean mIsHistoricalPackageUsageAvailable = true;
 
-        boolean isFirstBoot() {
-            return mIsFirstBoot;
+        boolean isHistoricalPackageUsageAvailable() {
+            return mIsHistoricalPackageUsageAvailable;
         }
 
         void write(boolean force) {
@@ -715,7 +715,7 @@
                         pkg.mLastPackageUsageTimeInMills = timeInMillis;
                     }
                 } catch (FileNotFoundException expected) {
-                    mIsFirstBoot = true;
+                    mIsHistoricalPackageUsageAvailable = false;
                 } catch (IOException e) {
                     Log.w(TAG, "Failed to read package usage times", e);
                 } finally {
@@ -1247,7 +1247,7 @@
         }
     }
 
-    public static final IPackageManager main(Context context, Installer installer,
+    public static final PackageManagerService main(Context context, Installer installer,
             boolean factoryTest, boolean onlyCore) {
         PackageManagerService m = new PackageManagerService(context, installer,
                 factoryTest, onlyCore);
@@ -1488,9 +1488,6 @@
                 // changed. Consider this significant, and wipe away all other
                 // existing dexopt files to ensure we don't leave any dangling around.
                 //
-                // Additionally, delete all dex files from the root directory
-                // since there shouldn't be any there anyway.
-                //
                 // TODO: This should be revisited because it isn't as good an indicator
                 // as it used to be. It used to include the boot classpath but at some point
                 // DexFile.isDexOptNeeded started returning false for the boot
@@ -1498,7 +1495,15 @@
                 // small maintenance release update that the library and tool
                 // jars may be unchanged but APK could be removed resulting in
                 // unused dalvik-cache files.
-                mInstaller.pruneDexCache();
+                for (String instructionSet : instructionSets) {
+                    mInstaller.pruneDexCache(instructionSet);
+                }
+
+                // Additionally, delete all dex files from the root directory
+                // since there shouldn't be any there anyway, unless we're upgrading
+                // from an older OS version or a build that contained the "old" style
+                // flat scheme.
+                mInstaller.pruneDexCache(".");
             }
 
             // Collect vendor overlay packages.
@@ -1742,7 +1747,7 @@
 
     @Override
     public boolean isFirstBoot() {
-        return !mRestoredSettings || mPackageUsage.isFirstBoot();
+        return !mRestoredSettings;
     }
 
     @Override
@@ -2242,11 +2247,12 @@
                 if ((flags & PackageManager.GET_UNINSTALLED_PACKAGES) == 0) {
                     return null;
                 }
+                // App code is gone, so we aren't worried about split paths
                 pkg = new PackageParser.Package(packageName);
                 pkg.applicationInfo.packageName = packageName;
                 pkg.applicationInfo.flags = ps.pkgFlags | ApplicationInfo.FLAG_IS_DATA_ONLY;
-                pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
                 pkg.applicationInfo.sourceDir = ps.codePathString;
+                pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
                 pkg.applicationInfo.dataDir =
                         getDataPathForPackage(packageName, 0).getPath();
                 pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
@@ -3345,7 +3351,7 @@
     }
 
     /*
-     * Returns if intent can be forwarded from the userId from to dest
+     * Returns if intent can be forwarded from the sourceUserId to the targetUserId
      */
     @Override
     public boolean canForwardTo(Intent intent, String resolvedType, int sourceUserId,
@@ -3360,14 +3366,34 @@
                 if (matches.get(i).getTargetUserId() == targetUserId) return true;
             }
         }
+
+        ArrayList<String> packageNames = null;
+        SparseArray<ArrayList<String>> fromSource =
+                mSettings.mCrossProfilePackageInfo.get(sourceUserId);
+        if (fromSource != null) {
+            packageNames = fromSource.get(targetUserId);
+        }
+        if (packageNames.contains(intent.getPackage())) {
+            return true;
+        }
+        // We need the package name, so we try to resolve with the loosest flags possible
+        List<ResolveInfo> resolveInfos = mActivities.queryIntent(
+                intent, resolvedType, PackageManager.GET_UNINSTALLED_PACKAGES, targetUserId);
+        int count = resolveInfos.size();
+        for (int i = 0; i < count; i++) {
+            ResolveInfo resolveInfo = resolveInfos.get(i);
+            if (packageNames.contains(resolveInfo.activityInfo.packageName)) {
+                return true;
+            }
+        }
         return false;
     }
 
     private List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(Intent intent,
             String resolvedType, int userId) {
-        CrossProfileIntentResolver cpir = mSettings.mCrossProfileIntentResolvers.get(userId);
-        if (cpir != null) {
-            return cpir.queryIntent(intent, resolvedType, false, userId);
+        CrossProfileIntentResolver resolver = mSettings.mCrossProfileIntentResolvers.get(userId);
+        if (resolver != null) {
+            return resolver.queryIntent(intent, resolvedType, false, userId);
         }
         return null;
     }
@@ -3400,41 +3426,44 @@
         synchronized (mPackages) {
             final String pkgName = intent.getPackage();
             if (pkgName == null) {
-                List<ResolveInfo> result =
-                        mActivities.queryIntent(intent, resolvedType, flags, userId);
-                // Checking if we can forward the intent to another user
-                List<CrossProfileIntentFilter> cpifs =
+                //Check if the intent needs to be forwarded to another user for this package
+                ArrayList<ResolveInfo> crossProfileResult =
+                        queryIntentActivitiesCrossProfilePackage(
+                                intent, resolvedType, flags, userId);
+                if (!crossProfileResult.isEmpty()) {
+                    // Skip the current profile
+                    return crossProfileResult;
+                }
+                List<ResolveInfo> result;
+                List<CrossProfileIntentFilter> matchingFilters =
                         getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);
-                if (cpifs != null) {
-                    CrossProfileIntentFilter crossProfileIntentFilterWithResult = null;
-                    HashSet<Integer> alreadyTriedUserIds = new HashSet<Integer>();
-                    for (CrossProfileIntentFilter cpif : cpifs) {
-                        int targetUserId = cpif.getTargetUserId();
-                        // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and
-                        // match the same an intent. For performance reasons, it is better not to
-                        // run queryIntent twice for the same userId
-                        if (!alreadyTriedUserIds.contains(targetUserId)) {
-                            List<ResolveInfo> resultUser = mActivities.queryIntent(intent,
-                                    resolvedType, flags, targetUserId);
-                            if (resultUser != null) {
-                                crossProfileIntentFilterWithResult = cpif;
-                                // As soon as there is a match in another user, we add the
-                                // intentForwarderActivity to the list of ResolveInfo.
-                                break;
-                            }
-                            alreadyTriedUserIds.add(targetUserId);
-                        }
-                    }
-                    if (crossProfileIntentFilterWithResult != null) {
-                        ResolveInfo forwardingResolveInfo = createForwardingResolveInfo(
-                                crossProfileIntentFilterWithResult, userId);
-                        result.add(forwardingResolveInfo);
-                    }
+                // Check for results that need to skip the current profile.
+                ResolveInfo resolveInfo = querySkipCurrentProfileIntents(matchingFilters, intent,
+                        resolvedType, flags, userId);
+                if (resolveInfo != null) {
+                    result = new ArrayList<ResolveInfo>(1);
+                    result.add(resolveInfo);
+                    return result;
+                }
+                // Check for results in the current profile.
+                result = mActivities.queryIntent(intent, resolvedType, flags, userId);
+                // Check for cross profile results.
+                resolveInfo = queryCrossProfileIntents(
+                        matchingFilters, intent, resolvedType, flags, userId);
+                if (resolveInfo != null) {
+                    result.add(resolveInfo);
                 }
                 return result;
             }
             final PackageParser.Package pkg = mPackages.get(pkgName);
             if (pkg != null) {
+                ArrayList<ResolveInfo> crossProfileResult =
+                        queryIntentActivitiesCrossProfilePackage(
+                                intent, resolvedType, flags, userId, pkg, pkgName);
+                if (!crossProfileResult.isEmpty()) {
+                    // Skip the current profile
+                    return crossProfileResult;
+                }
                 return mActivities.queryIntentForPackage(intent, resolvedType, flags,
                         pkg.activities, userId);
             }
@@ -3442,12 +3471,121 @@
         }
     }
 
-    private ResolveInfo createForwardingResolveInfo(CrossProfileIntentFilter cpif,
-            int sourceUserId) {
+    private ResolveInfo querySkipCurrentProfileIntents(
+            List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
+            int flags, int sourceUserId) {
+        if (matchingFilters != null) {
+            int size = matchingFilters.size();
+            for (int i = 0; i < size; i ++) {
+                CrossProfileIntentFilter filter = matchingFilters.get(i);
+                if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) != 0) {
+                    // Checking if there are activities in the target user that can handle the
+                    // intent.
+                    ResolveInfo resolveInfo = checkTargetCanHandle(filter, intent, resolvedType,
+                            flags, sourceUserId);
+                    if (resolveInfo != null) {
+                        return resolveInfo;
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    private ArrayList<ResolveInfo> queryIntentActivitiesCrossProfilePackage(
+            Intent intent, String resolvedType, int flags, int userId) {
+        ArrayList<ResolveInfo> matchingResolveInfos = new ArrayList<ResolveInfo>();
+        SparseArray<ArrayList<String>> sourceForwardingInfo =
+                mSettings.mCrossProfilePackageInfo.get(userId);
+        if (sourceForwardingInfo != null) {
+            int NI = sourceForwardingInfo.size();
+            for (int i = 0; i < NI; i++) {
+                int targetUserId = sourceForwardingInfo.keyAt(i);
+                ArrayList<String> packageNames = sourceForwardingInfo.valueAt(i);
+                List<ResolveInfo> resolveInfos = mActivities.queryIntent(
+                        intent, resolvedType, flags, targetUserId);
+                int NJ = resolveInfos.size();
+                for (int j = 0; j < NJ; j++) {
+                    ResolveInfo resolveInfo = resolveInfos.get(j);
+                    if (packageNames.contains(resolveInfo.activityInfo.packageName)) {
+                        matchingResolveInfos.add(createForwardingResolveInfo(
+                                resolveInfo.filter, userId, targetUserId));
+                    }
+                }
+            }
+        }
+        return matchingResolveInfos;
+    }
+
+    private ArrayList<ResolveInfo> queryIntentActivitiesCrossProfilePackage(
+            Intent intent, String resolvedType, int flags, int userId, PackageParser.Package pkg,
+            String packageName) {
+        ArrayList<ResolveInfo> matchingResolveInfos = new ArrayList<ResolveInfo>();
+        SparseArray<ArrayList<String>> sourceForwardingInfo =
+                mSettings.mCrossProfilePackageInfo.get(userId);
+        if (sourceForwardingInfo != null) {
+            int NI = sourceForwardingInfo.size();
+            for (int i = 0; i < NI; i++) {
+                int targetUserId = sourceForwardingInfo.keyAt(i);
+                if (sourceForwardingInfo.valueAt(i).contains(packageName)) {
+                    List<ResolveInfo> resolveInfos = mActivities.queryIntentForPackage(
+                            intent, resolvedType, flags, pkg.activities, targetUserId);
+                    int NJ = resolveInfos.size();
+                    for (int j = 0; j < NJ; j++) {
+                        ResolveInfo resolveInfo = resolveInfos.get(j);
+                        matchingResolveInfos.add(createForwardingResolveInfo(
+                                resolveInfo.filter, userId, targetUserId));
+                    }
+                }
+            }
+        }
+        return matchingResolveInfos;
+    }
+
+    // Return matching ResolveInfo if any for skip current profile intent filters.
+    private ResolveInfo queryCrossProfileIntents(
+            List<CrossProfileIntentFilter> matchingFilters, Intent intent, String resolvedType,
+            int flags, int sourceUserId) {
+        if (matchingFilters != null) {
+            // Two {@link CrossProfileIntentFilter}s can have the same targetUserId and
+            // match the same intent. For performance reasons, it is better not to
+            // run queryIntent twice for the same userId
+            SparseBooleanArray alreadyTriedUserIds = new SparseBooleanArray();
+            int size = matchingFilters.size();
+            for (int i = 0; i < size; i++) {
+                CrossProfileIntentFilter filter = matchingFilters.get(i);
+                int targetUserId = filter.getTargetUserId();
+                if ((filter.getFlags() & PackageManager.SKIP_CURRENT_PROFILE) == 0
+                        && !alreadyTriedUserIds.get(targetUserId)) {
+                    // Checking if there are activities in the target user that can handle the
+                    // intent.
+                    ResolveInfo resolveInfo = checkTargetCanHandle(filter, intent, resolvedType,
+                            flags, sourceUserId);
+                    if (resolveInfo != null) return resolveInfo;
+                    alreadyTriedUserIds.put(targetUserId, true);
+                }
+            }
+        }
+        return null;
+    }
+
+    private ResolveInfo checkTargetCanHandle(CrossProfileIntentFilter filter, Intent intent,
+            String resolvedType, int flags, int sourceUserId) {
+        List<ResolveInfo> resultTargetUser = mActivities.queryIntent(intent,
+                resolvedType, flags, filter.getTargetUserId());
+        if (resultTargetUser != null) {
+            return createForwardingResolveInfo(filter, sourceUserId, filter.getTargetUserId());
+        }
+        return null;
+    }
+
+    private ResolveInfo createForwardingResolveInfo(IntentFilter filter,
+            int sourceUserId, int targetUserId) {
+        ResolveInfo forwardingResolveInfo = new ResolveInfo();
         String className;
-        int targetUserId = cpif.getTargetUserId();
         if (targetUserId == UserHandle.USER_OWNER) {
             className = FORWARD_INTENT_TO_USER_OWNER;
+            forwardingResolveInfo.showTargetUserIcon = true;
         } else {
             className = FORWARD_INTENT_TO_MANAGED_PROFILE;
         }
@@ -3455,13 +3593,13 @@
                 mAndroidApplication.packageName, className);
         ActivityInfo forwardingActivityInfo = getActivityInfo(forwardingActivityComponentName, 0,
                 sourceUserId);
-        ResolveInfo forwardingResolveInfo = new ResolveInfo();
         forwardingResolveInfo.activityInfo = forwardingActivityInfo;
         forwardingResolveInfo.priority = 0;
         forwardingResolveInfo.preferredOrder = 0;
         forwardingResolveInfo.match = 0;
         forwardingResolveInfo.isDefault = true;
-        forwardingResolveInfo.filter = cpif;
+        forwardingResolveInfo.filter = filter;
+        forwardingResolveInfo.targetUserId = targetUserId;
         return forwardingResolveInfo;
     }
 
@@ -4081,6 +4219,7 @@
             return false;
         }
         final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+        // TODO: generate idmap for split APKs
         if (mInstaller.idmap(pkg.codePath, opkg.codePath, sharedGid) != 0) {
             Slog.e(TAG, "Failed to generate idmap for " + pkg.codePath + " and " + opkg.codePath);
             return false;
@@ -4179,6 +4318,7 @@
 
         try {
             pp.collectCertificates(pkg, parseFlags);
+            pp.collectManifestDigest(pkg);
         } catch (PackageParserException e) {
             mLastScanError = e.error;
             return false;
@@ -4196,14 +4336,18 @@
         String scanPath = scanFile.getPath();
         if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanPath);
         parseFlags |= mDefParseFlags;
-        PackageParser pp = new PackageParser(scanPath);
+        PackageParser pp = new PackageParser();
         pp.setSeparateProcesses(mSeparateProcesses);
         pp.setOnlyCoreApps(mOnlyCore);
+        pp.setDisplayMetrics(mMetrics);
+
+        if ((scanMode & SCAN_TRUSTED_OVERLAY) != 0) {
+            parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
+        }
 
         final PackageParser.Package pkg;
         try {
-            pkg = pp.parseMonolithicPackage(scanFile, mMetrics, parseFlags,
-                (scanMode & SCAN_TRUSTED_OVERLAY) != 0);
+            pkg = pp.parseMonolithicPackage(scanFile, parseFlags);
         } catch (PackageParserException e) {
             mLastScanError = e.error;
             return null;
@@ -4362,23 +4506,30 @@
             }
         }
 
-        String codePath = null;
+        final String codePath = pkg.codePath;
+        final String[] splitCodePaths = pkg.splitCodePaths;
+
         String resPath = null;
+        String[] splitResPaths = null;
         if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
             if (ps != null && ps.resourcePathString != null) {
                 resPath = ps.resourcePathString;
+                splitResPaths = deriveSplitResPaths(pkg.splitCodePaths);
             } else {
                 // Should not happen at all. Just log an error.
                 Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
             }
         } else {
             resPath = pkg.codePath;
+            splitResPaths = pkg.splitCodePaths;
         }
 
-        codePath = pkg.codePath;
         // Set application objects path explicitly.
         pkg.applicationInfo.sourceDir = codePath;
         pkg.applicationInfo.publicSourceDir = resPath;
+        pkg.applicationInfo.splitSourceDirs = splitCodePaths;
+        pkg.applicationInfo.splitPublicSourceDirs = splitResPaths;
+
         // Note that we invoke the following method only if we are about to unpack an application
         PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
                 | SCAN_UPDATE_SIGNATURE, currentTime, user, abiOverride);
@@ -4477,7 +4628,7 @@
             // The exception is first boot of a non-eng device, which
             // should do a full dexopt.
             boolean eng = "eng".equals(SystemProperties.get("ro.build.type"));
-            if (eng || !isFirstBoot()) {
+            if (eng || (!isFirstBoot() && mPackageUsage.isHistoricalPackageUsageAvailable())) {
                 // TODO: add a property to control this?
                 long dexOptLRUThresholdInMinutes;
                 if (eng) {
@@ -4626,52 +4777,51 @@
             }
         }
 
-        boolean performed = false;
-        if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
-            String path = pkg.codePath;
-            try {
-                boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path,
-                                                                                pkg.packageName,
-                                                                                instructionSet,
-                                                                                defer);
-                // There are three basic cases here:
-                // 1.) we need to dexopt, either because we are forced or it is needed
-                // 2.) we are defering a needed dexopt
-                // 3.) we are skipping an unneeded dexopt
-                if (forceDex || (!defer && isDexOptNeededInternal)) {
-                    Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName);
-                    final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
-                    int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
-                                                pkg.packageName, instructionSet);
-                    // Note that we ran dexopt, since rerunning will
-                    // probably just result in an error again.
+        if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
+            final Collection<String> paths = pkg.getAllCodePaths();
+            for (String path : paths) {
+                try {
+                    boolean isDexOptNeededInternal = DexFile.isDexOptNeededInternal(path,
+                            pkg.packageName, instructionSet, defer);
+                    // There are three basic cases here:
+                    // 1.) we need to dexopt, either because we are forced or it is needed
+                    // 2.) we are defering a needed dexopt
+                    // 3.) we are skipping an unneeded dexopt
+                    if (forceDex || (!defer && isDexOptNeededInternal)) {
+                        Log.i(TAG, "Running dexopt on: " + pkg.applicationInfo.packageName);
+                        final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
+                        int ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg),
+                                                    pkg.packageName, instructionSet);
+                        // Note that we ran dexopt, since rerunning will
+                        // probably just result in an error again.
+                        pkg.mDexOptNeeded = false;
+                        if (ret < 0) {
+                            return DEX_OPT_FAILED;
+                        }
+                        return DEX_OPT_PERFORMED;
+                    }
+                    if (defer && isDexOptNeededInternal) {
+                        if (mDeferredDexOpt == null) {
+                            mDeferredDexOpt = new HashSet<PackageParser.Package>();
+                        }
+                        mDeferredDexOpt.add(pkg);
+                        return DEX_OPT_DEFERRED;
+                    }
                     pkg.mDexOptNeeded = false;
-                    if (ret < 0) {
-                        return DEX_OPT_FAILED;
-                    }
-                    return DEX_OPT_PERFORMED;
+                    return DEX_OPT_SKIPPED;
+                } catch (FileNotFoundException e) {
+                    Slog.w(TAG, "Apk not found for dexopt: " + path);
+                    return DEX_OPT_FAILED;
+                } catch (IOException e) {
+                    Slog.w(TAG, "IOException reading apk: " + path, e);
+                    return DEX_OPT_FAILED;
+                } catch (StaleDexCacheError e) {
+                    Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e);
+                    return DEX_OPT_FAILED;
+                } catch (Exception e) {
+                    Slog.w(TAG, "Exception when doing dexopt : ", e);
+                    return DEX_OPT_FAILED;
                 }
-                if (defer && isDexOptNeededInternal) {
-                    if (mDeferredDexOpt == null) {
-                        mDeferredDexOpt = new HashSet<PackageParser.Package>();
-                    }
-                    mDeferredDexOpt.add(pkg);
-                    return DEX_OPT_DEFERRED;
-                }
-                pkg.mDexOptNeeded = false;
-                return DEX_OPT_SKIPPED;
-            } catch (FileNotFoundException e) {
-                Slog.w(TAG, "Apk not found for dexopt: " + path);
-                return DEX_OPT_FAILED;
-            } catch (IOException e) {
-                Slog.w(TAG, "IOException reading apk: " + path, e);
-                return DEX_OPT_FAILED;
-            } catch (StaleDexCacheError e) {
-                Slog.w(TAG, "StaleDexCacheError when reading apk: " + path, e);
-                return DEX_OPT_FAILED;
-            } catch (Exception e) {
-                Slog.w(TAG, "Exception when doing dexopt : ", e);
-                return DEX_OPT_FAILED;
             }
         }
         return DEX_OPT_SKIPPED;
@@ -4818,7 +4968,7 @@
             }
         }
         if (p != null) {
-            usesLibraryFiles.add(p.codePath);
+            usesLibraryFiles.addAll(p.getAllCodePaths());
         }
     }
 
@@ -4906,7 +5056,7 @@
     private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
             int parseFlags, int scanMode, long currentTime, UserHandle user, String abiOverride) {
         final File scanFile = new File(pkg.codePath);
-        if (scanFile == null || pkg.applicationInfo.sourceDir == null ||
+        if (pkg.applicationInfo.sourceDir == null ||
                 pkg.applicationInfo.publicSourceDir == null) {
             // Bail out. The resource and code paths haven't been set.
             Slog.w(TAG, " Code and resource paths haven't been set correctly");
@@ -5355,6 +5505,7 @@
          *        only for non-system apps and system app upgrades.
          */
         if (pkg.applicationInfo.nativeLibraryDir != null) {
+            // TODO: extend to extract native code from split APKs
             final NativeLibraryHelper.ApkHandle handle = new NativeLibraryHelper.ApkHandle(scanFile);
             try {
                 // Enable gross and lame hacks for apps that are built with old
@@ -5656,7 +5807,8 @@
             try {
                 ksm.addSigningKeySetToPackage(pkg.packageName, pkg.mSigningKeys);
                 if (pkg.mKeySetMapping != null) {
-                    for (Map.Entry<String, Set<PublicKey>> entry : pkg.mKeySetMapping.entrySet()) {
+                    for (Map.Entry<String, ArraySet<PublicKey>> entry :
+                            pkg.mKeySetMapping.entrySet()) {
                         if (entry.getValue() != null) {
                             ksm.addDefinedKeySetToPackage(pkg.packageName,
                                 entry.getValue(), entry.getKey());
@@ -5908,6 +6060,8 @@
                 a.info.packageName = pkg.applicationInfo.packageName;
                 a.info.sourceDir = pkg.applicationInfo.sourceDir;
                 a.info.publicSourceDir = pkg.applicationInfo.publicSourceDir;
+                a.info.splitSourceDirs = pkg.applicationInfo.splitSourceDirs;
+                a.info.splitPublicSourceDirs = pkg.applicationInfo.splitPublicSourceDirs;
                 a.info.dataDir = pkg.applicationInfo.dataDir;
                 a.info.nativeLibraryDir = pkg.applicationInfo.nativeLibraryDir;
                 mInstrumentation.put(a.getComponentName(), a);
@@ -9021,6 +9175,10 @@
         abstract boolean doPostDeleteLI(boolean delete);
         abstract boolean checkFreeStorage(IMediaContainerService imcs) throws RemoteException;
 
+        String[] getSplitCodePaths() {
+            return null;
+        }
+
         /**
          * Called before the source arguments are copied. This is used mostly
          * for MoveParams when it needs to read the source file to put it in the
@@ -9109,10 +9267,6 @@
             }
         }
 
-        String getCodePath() {
-            return codeFileName;
-        }
-
         void createCopyFile() {
             installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
             codeFileName = createTempPackageFile(installDir).getPath();
@@ -9268,10 +9422,6 @@
             return status;
         }
 
-        String getResourcePath() {
-            return resourceFileName;
-        }
-
         private String getResourcePathFromCodePath() {
             final String codePath = getCodePath();
             if (isFwdLocked()) {
@@ -9302,6 +9452,16 @@
         }
 
         @Override
+        String getCodePath() {
+            return codeFileName;
+        }
+
+        @Override
+        String getResourcePath() {
+            return resourceFileName;
+        }
+
+        @Override
         String getNativeLibraryPath() {
             if (libraryPath == null) {
                 libraryPath = getLibraryPathFromCodePath();
@@ -9687,7 +9847,7 @@
 
             return PackageManager.INSTALL_SUCCEEDED;
         }
-    };
+    }
 
     static String getAsecPackageName(String packageCid) {
         int idx = packageCid.lastIndexOf("-");
@@ -9768,6 +9928,20 @@
         return codePath.substring(sidx+1, eidx);
     }
 
+    private static String[] deriveSplitResPaths(String[] splitCodePaths) {
+        String[] splitResPaths = null;
+        if (!ArrayUtils.isEmpty(splitCodePaths)) {
+            splitResPaths = new String[splitCodePaths.length];
+            for (int i = 0; i < splitCodePaths.length; i++) {
+                final String splitCodePath = splitCodePaths[i];
+                final String resName = getApkName(splitCodePath) + ".zip";
+                splitResPaths[i] = new File(new File(splitCodePath).getParentFile(),
+                        resName).getAbsolutePath();
+            }
+        }
+        return splitResPaths;
+    }
+
     class PackageInstalledInfo {
         String name;
         int uid;
@@ -10067,6 +10241,7 @@
 
     // Utility method used to move dex files during install.
     private int moveDexFilesLI(String oldCodePath, PackageParser.Package newPackage) {
+        // TODO: extend to move split APK dex files
         if ((newPackage.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
             final String instructionSet = getAppInstructionSet(newPackage.applicationInfo);
             int retCode = mInstaller.movedex(oldCodePath, newPackage.codePath,
@@ -10165,13 +10340,13 @@
         int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
                 | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
                 | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
-        PackageParser pp = new PackageParser(tmpPackageFile.getPath());
+        PackageParser pp = new PackageParser();
         pp.setSeparateProcesses(mSeparateProcesses);
+        pp.setDisplayMetrics(mMetrics);
 
         final PackageParser.Package pkg;
         try {
-            pkg = pp.parseMonolithicPackage(tmpPackageFile, mMetrics,
-                parseFlags);
+            pkg = pp.parseMonolithicPackage(tmpPackageFile, parseFlags);
         } catch (PackageParserException e) {
             res.returnCode = e.error;
             return;
@@ -10187,6 +10362,7 @@
 
         try {
             pp.collectCertificates(pkg, parseFlags);
+            pp.collectManifestDigest(pkg);
         } catch (PackageParserException e) {
             res.returnCode = e.error;
             return;
@@ -10296,6 +10472,9 @@
         pkg.codePath = args.getCodePath();
         pkg.applicationInfo.sourceDir = args.getCodePath();
         pkg.applicationInfo.publicSourceDir = args.getResourcePath();
+        pkg.applicationInfo.splitSourceDirs = args.getSplitCodePaths();
+        pkg.applicationInfo.splitPublicSourceDirs = deriveSplitResPaths(
+                pkg.applicationInfo.splitSourceDirs);
         pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();
         if (replace) {
             replacePackageLI(pkg, parseFlags, scanMode, args.user,
@@ -11498,32 +11677,51 @@
     }
 
     @Override
-    public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable,
-            int sourceUserId, int targetUserId) {
+    public void addCrossProfileIntentFilter(IntentFilter intentFilter, int sourceUserId,
+            int targetUserId, int flags) {
         mContext.enforceCallingOrSelfPermission(
                         android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
-        if (filter.countActions() == 0) {
+        if (intentFilter.countActions() == 0) {
             Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions");
             return;
         }
         synchronized (mPackages) {
-            mSettings.editCrossProfileIntentResolverLPw(sourceUserId).addFilter(
-                    new CrossProfileIntentFilter(filter, removable, targetUserId));
+            CrossProfileIntentFilter filter = new CrossProfileIntentFilter(intentFilter,
+                    targetUserId, flags);
+            mSettings.editCrossProfileIntentResolverLPw(sourceUserId).addFilter(filter);
             mSettings.writePackageRestrictionsLPr(sourceUserId);
         }
     }
 
+    public void addCrossProfileIntentsForPackage(String packageName,
+            int sourceUserId, int targetUserId) {
+        mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+        mSettings.addCrossProfilePackage(packageName, sourceUserId, targetUserId);
+        mSettings.writePackageRestrictionsLPr(sourceUserId);
+    }
+
+    public void removeCrossProfileIntentsForPackage(String packageName,
+            int sourceUserId, int targetUserId) {
+        mContext.enforceCallingOrSelfPermission(
+                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
+        mSettings.removeCrossProfilePackage(packageName, sourceUserId, targetUserId);
+        mSettings.writePackageRestrictionsLPr(sourceUserId);
+    }
+
     @Override
     public void clearCrossProfileIntentFilters(int sourceUserId) {
         mContext.enforceCallingOrSelfPermission(
                         android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
         synchronized (mPackages) {
-            CrossProfileIntentResolver cpir =
+            CrossProfileIntentResolver resolver =
                     mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
             HashSet<CrossProfileIntentFilter> set =
-                    new HashSet<CrossProfileIntentFilter>(cpir.filterSet());
-            for (CrossProfileIntentFilter cpif : set) {
-                if (cpif.isRemovable()) cpir.removeFilter(cpif);
+                    new HashSet<CrossProfileIntentFilter>(resolver.filterSet());
+            for (CrossProfileIntentFilter filter : set) {
+                if ((filter.getFlags() & PackageManager.SET_BY_PROFILE_OWNER) != 0) {
+                    resolver.removeFilter(filter);
+                }
             }
             mSettings.writePackageRestrictionsLPr(sourceUserId);
         }
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 3483fae..1867ff3 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -31,12 +31,14 @@
 import android.net.Uri;
 import android.os.PatternMatcher;
 import android.util.LogPrinter;
+
 import com.android.internal.util.FastXmlSerializer;
 import com.android.internal.util.JournaledFile;
 import com.android.internal.util.XmlUtils;
 import com.android.server.pm.PackageManagerService.DumpState;
 
 import java.util.Collection;
+
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 import org.xmlpull.v1.XmlSerializer;
@@ -134,6 +136,10 @@
             "persistent-preferred-activities";
     static final String TAG_CROSS_PROFILE_INTENT_FILTERS =
             "crossProfile-intent-filters";
+    private static final String TAG_CROSS_PROFILE_PACKAGE_INFO = "cross-profile-package-info";
+    private static final String CROSS_PROFILE_PACKAGE_INFO_ATTR_TARGET_USER_ID = "target-user-id";
+    private static final String CROSS_PROFILE_PACKAGE_INFO_TAG_PACKAGE_NAME = "package-name";
+    private static final String CROSS_PROFILE_PACKAGE_INFO_ATTR_PACKAGE_NAME = "value";
     //Old name. Kept for compatibility
     static final String TAG_FORWARDING_INTENT_FILTERS =
             "forwarding-intent-filters";
@@ -240,6 +246,11 @@
 
     public final KeySetManager mKeySetManager = new KeySetManager(mPackages);
 
+    // A mapping of (sourceUserId, targetUserId, packageNames) for forwarding the intents of a
+    // package.
+    final SparseArray<SparseArray<ArrayList<String>>>
+            mCrossProfilePackageInfo = new SparseArray<SparseArray<ArrayList<String>>>();
+
     Settings(Context context) {
         this(context, Environment.getDataDirectory());
     }
@@ -262,6 +273,47 @@
         mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
     }
 
+    public void addCrossProfilePackage(
+            String packageName, int sourceUserId, int targetUserId) {
+        synchronized(mCrossProfilePackageInfo) {
+            SparseArray<ArrayList<String>> sourceForwardingInfo =
+                    mCrossProfilePackageInfo.get(sourceUserId);
+            if (sourceForwardingInfo == null) {
+                sourceForwardingInfo = new SparseArray<ArrayList<String>>();
+                mCrossProfilePackageInfo.put(sourceUserId, sourceForwardingInfo);
+            }
+            ArrayList<String> packageNames = sourceForwardingInfo.get(targetUserId);
+            if (packageNames == null) {
+                packageNames = new ArrayList<String>();
+                sourceForwardingInfo.put(targetUserId, packageNames);
+            }
+            if (!packageNames.contains(packageName)) {
+                packageNames.add(packageName);
+            }
+        }
+    }
+
+    public void removeCrossProfilePackage(
+            String packageName, int sourceUserId, int targetUserId) {
+        synchronized(mCrossProfilePackageInfo) {
+            SparseArray<ArrayList<String>> sourceForwardingInfo =
+                    mCrossProfilePackageInfo.get(sourceUserId);
+            if (sourceForwardingInfo == null) {
+                return;
+            }
+            ArrayList<String> packageNames = sourceForwardingInfo.get(targetUserId);
+            if (packageNames != null && packageNames.contains(packageName)) {
+                packageNames.remove(packageName);
+                if (packageNames.isEmpty()) {
+                    sourceForwardingInfo.remove(targetUserId);
+                    if (sourceForwardingInfo.size() == 0) {
+                        mCrossProfilePackageInfo.remove(sourceUserId);
+                    }
+                }
+            }
+        }
+    }
+
     PackageSetting getPackageLPw(PackageParser.Package pkg, PackageSetting origPackage,
             String realName, SharedUserSetting sharedUser, File codePath, File resourcePath,
             String nativeLibraryPathString, String cpuAbiString, int pkgFlags, UserHandle user, boolean add) {
@@ -1005,6 +1057,68 @@
         }
     }
 
+    private void readCrossProfilePackageInfoLPw(XmlPullParser parser, int userId)
+            throws XmlPullParserException, IOException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            String tagName = parser.getName();
+            if (tagName.equals(TAG_ITEM)) {
+                String targetUserIdString = parser.getAttributeValue(null,
+                        CROSS_PROFILE_PACKAGE_INFO_ATTR_TARGET_USER_ID);
+                if (targetUserIdString == null) {
+                    String msg = "Missing element under " + TAG +": "
+                            + CROSS_PROFILE_PACKAGE_INFO_ATTR_TARGET_USER_ID + " at " +
+                            parser.getPositionDescription();
+                    PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+                    continue;
+                }
+                int targetUserId = Integer.parseInt(targetUserIdString);
+                readCrossProfilePackageInfoForTargetLPw(parser, userId, targetUserId);
+            } else {
+                String msg = "Unknown element under " +  TAG_CROSS_PROFILE_PACKAGE_INFO + ": " +
+                        parser.getName();
+                PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+    }
+
+    private void readCrossProfilePackageInfoForTargetLPw(
+            XmlPullParser parser, int sourceUserId, int targetUserId)
+            throws XmlPullParserException, IOException {
+        int outerDepth = parser.getDepth();
+        int type;
+        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+                && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+            if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+                continue;
+            }
+            String tagName = parser.getName();
+            if (tagName.equals(CROSS_PROFILE_PACKAGE_INFO_TAG_PACKAGE_NAME)) {
+                String packageName = parser.getAttributeValue(
+                        null, CROSS_PROFILE_PACKAGE_INFO_ATTR_PACKAGE_NAME);
+                if (packageName == null) {
+                    String msg = "Missing element under " + TAG +": "
+                            + CROSS_PROFILE_PACKAGE_INFO_TAG_PACKAGE_NAME + " at " +
+                            parser.getPositionDescription();
+                    PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+                    continue;
+                }
+                addCrossProfilePackage(packageName, sourceUserId, targetUserId);
+            } else {
+                String msg = "Unknown element under " +  TAG_ITEM + ": " +
+                        parser.getName();
+                PackageManagerService.reportSettingsProblem(Log.WARN, msg);
+                XmlUtils.skipCurrentTag(parser);
+            }
+        }
+    }
+
     void readPackageRestrictionsLPr(int userId) {
         if (DEBUG_MU) {
             Log.i(TAG, "Reading package restrictions for user=" + userId);
@@ -1136,6 +1250,8 @@
                 } else if (tagName.equals(TAG_FORWARDING_INTENT_FILTERS)
                         || tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {
                     readCrossProfileIntentFiltersLPw(parser, userId);
+                } else if (tagName.equals(TAG_CROSS_PROFILE_PACKAGE_INFO)){
+                    readCrossProfilePackageInfoLPw(parser, userId);
                 } else {
                     Slog.w(PackageManagerService.TAG, "Unknown element under <stopped-packages>: "
                           + parser.getName());
@@ -1227,6 +1343,32 @@
         serializer.endTag(null, TAG_CROSS_PROFILE_INTENT_FILTERS);
     }
 
+    void writeCrossProfilePackageInfoLPr(XmlSerializer serializer, int userId)
+            throws IllegalArgumentException, IllegalStateException, IOException {
+        SparseArray<ArrayList<String>> sourceForwardingInfo = mCrossProfilePackageInfo.get(userId);
+        if (sourceForwardingInfo == null) {
+            return;
+        }
+        serializer.startTag(null, TAG_CROSS_PROFILE_PACKAGE_INFO);
+        int NI = sourceForwardingInfo.size();
+        for (int i = 0; i < NI; i++) {
+            int targetUserId = sourceForwardingInfo.keyAt(i);
+            ArrayList<String> packageNames = sourceForwardingInfo.valueAt(i);
+            serializer.startTag(null, TAG_ITEM);
+            serializer.attribute(null, CROSS_PROFILE_PACKAGE_INFO_ATTR_TARGET_USER_ID,
+                    Integer.toString(targetUserId));
+            int NJ = packageNames.size();
+            for (int j = 0; j < NJ; j++) {
+                serializer.startTag(null, CROSS_PROFILE_PACKAGE_INFO_TAG_PACKAGE_NAME);
+                serializer.attribute(null, CROSS_PROFILE_PACKAGE_INFO_ATTR_PACKAGE_NAME,
+                        packageNames.get(j));
+                serializer.endTag(null, CROSS_PROFILE_PACKAGE_INFO_TAG_PACKAGE_NAME);
+            }
+            serializer.endTag(null, TAG_ITEM);
+        }
+        serializer.endTag(null, TAG_CROSS_PROFILE_PACKAGE_INFO);
+    }
+
     void writePackageRestrictionsLPr(int userId) {
         if (DEBUG_MU) {
             Log.i(TAG, "Writing package restrictions for user=" + userId);
@@ -1327,6 +1469,8 @@
 
             writeCrossProfileIntentFiltersLPr(serializer, userId);
 
+            writeCrossProfilePackageInfoLPr(serializer, userId);
+
             serializer.endTag(null, TAG_PACKAGE_RESTRICTIONS);
 
             serializer.endDocument();
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b941657..a0cb098 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -412,13 +412,17 @@
 
     @Override
     public Bitmap getUserIcon(int userId) {
-        checkManageUsersPermission("read users");
         synchronized (mPackagesLock) {
             UserInfo info = mUsers.get(userId);
             if (info == null || info.partial) {
                 Slog.w(LOG_TAG, "getUserIcon: unknown user #" + userId);
                 return null;
             }
+            int callingGroupId = mUsers.get(UserHandle.getCallingUserId()).profileGroupId;
+            if (callingGroupId == UserInfo.NO_PROFILE_GROUP_ID
+                    || callingGroupId != info.profileGroupId) {
+                checkManageUsersPermission("get the icon of a user who is not related");
+            }
             if (info.iconPath == null) {
                 return null;
             }
@@ -744,7 +748,7 @@
                 writeBoolean(serializer, restrictions, UserManager.ENSURE_VERIFY_APPS);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
-                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_APPS);
+                writeBoolean(serializer, restrictions, UserManager.DISALLOW_APPS_CONTROL);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
                 writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
@@ -896,7 +900,7 @@
                         readBoolean(parser, restrictions, UserManager.ENSURE_VERIFY_APPS);
                         readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
                         readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
-                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_APPS);
+                        readBoolean(parser, restrictions, UserManager.DISALLOW_APPS_CONTROL);
                         readBoolean(parser, restrictions,
                                 UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
                         readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index fb4b8f0..bd80b54 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -19,10 +19,10 @@
 import com.android.internal.app.IAppOpsService;
 import com.android.internal.app.IBatteryStats;
 import com.android.internal.os.BackgroundThread;
-import com.android.server.BatteryService;
 import com.android.server.EventLogTags;
 import com.android.server.LocalServices;
 import com.android.server.ServiceThread;
+import com.android.server.am.BatteryStatsService;
 import com.android.server.lights.Light;
 import com.android.server.lights.LightsManager;
 import com.android.server.Watchdog;
@@ -42,6 +42,7 @@
 import android.hardware.display.DisplayManagerInternal.DisplayPowerRequest;
 import android.net.Uri;
 import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
 import android.os.Binder;
 import android.os.Handler;
 import android.os.IBinder;
@@ -163,13 +164,14 @@
     private static final int POWER_HINT_LOW_POWER_MODE = 5;
 
     private final Context mContext;
+    private final ServiceThread mHandlerThread;
+    private final PowerManagerHandler mHandler;
+
     private LightsManager mLightsManager;
-    private BatteryService mBatteryService;
+    private BatteryManagerInternal mBatteryManagerInternal;
     private DisplayManagerInternal mDisplayManagerInternal;
     private IBatteryStats mBatteryStats;
     private IAppOpsService mAppOps;
-    private ServiceThread mHandlerThread;
-    private PowerManagerHandler mHandler;
     private WindowManagerPolicy mPolicy;
     private Notifier mNotifier;
     private WirelessChargerDetector mWirelessChargerDetector;
@@ -429,6 +431,11 @@
     public PowerManagerService(Context context) {
         super(context);
         mContext = context;
+        mHandlerThread = new ServiceThread(TAG,
+                Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
+        mHandlerThread.start();
+        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
+
         synchronized (mLock) {
             mWakeLockSuspendBlocker = createSuspendBlockerLocked("PowerManagerService.WakeLocks");
             mDisplaySuspendBlocker = createSuspendBlockerLocked("PowerManagerService.Display");
@@ -451,39 +458,19 @@
     public void onStart() {
         publishBinderService(Context.POWER_SERVICE, new BinderService());
         publishLocalService(PowerManagerInternal.class, new LocalService());
-    }
-
-    /**
-     * Initialize the power manager.
-     * Must be called before any other functions within the power manager are called.
-     */
-    public void init(LightsManager ls,
-            BatteryService bs, IBatteryStats bss,
-            IAppOpsService appOps) {
-        mLightsManager = ls;
-        mBatteryService = bs;
-        mBatteryStats = bss;
-        mAppOps = appOps;
-        mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
-        mHandlerThread = new ServiceThread(TAG,
-                Process.THREAD_PRIORITY_DISPLAY, false /*allowIo*/);
-        mHandlerThread.start();
-        mHandler = new PowerManagerHandler(mHandlerThread.getLooper());
 
         Watchdog.getInstance().addMonitor(this);
         Watchdog.getInstance().addThread(mHandler);
     }
 
-    void setPolicy(WindowManagerPolicy policy) {
-        synchronized (mLock) {
-            mPolicy = policy;
-        }
-    }
-
-    public void systemReady() {
+    public void systemReady(IAppOpsService appOps) {
         synchronized (mLock) {
             mSystemReady = true;
-            mDreamManager = LocalServices.getService(DreamManagerInternal.class);
+            mAppOps = appOps;
+            mDreamManager = getLocalService(DreamManagerInternal.class);
+            mDisplayManagerInternal = getLocalService(DisplayManagerInternal.class);
+            mPolicy = getLocalService(WindowManagerPolicy.class);
+            mBatteryManagerInternal = getLocalService(BatteryManagerInternal.class);
 
             PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
             mScreenBrightnessSettingMinimum = pm.getMinimumScreenBrightnessSetting();
@@ -494,6 +481,7 @@
 
             // The notifier runs on the system server's main looper so as not to interfere
             // with the animations and other critical functions of the power manager.
+            mBatteryStats = BatteryStatsService.getService();
             mNotifier = new Notifier(Looper.getMainLooper(), mContext, mBatteryStats,
                     mAppOps, createSuspendBlockerLocked("PowerManagerService.Broadcasts"),
                     mScreenOnBlocker, mPolicy);
@@ -502,6 +490,8 @@
                     createSuspendBlockerLocked("PowerManagerService.WirelessChargerDetector"),
                     mHandler);
             mSettingsObserver = new SettingsObserver(mHandler);
+
+            mLightsManager = getLocalService(LightsManager.class);
             mAttentionLight = mLightsManager.getLight(LightsManager.LIGHT_ID_ATTENTION);
 
             // Initialize display power management.
@@ -1168,10 +1158,10 @@
             final boolean wasPowered = mIsPowered;
             final int oldPlugType = mPlugType;
             final boolean oldLevelLow = mBatteryLevelLow;
-            mIsPowered = mBatteryService.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
-            mPlugType = mBatteryService.getPlugType();
-            mBatteryLevel = mBatteryService.getBatteryLevel();
-            mBatteryLevelLow = mBatteryService.getBatteryLevelLow();
+            mIsPowered = mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_ANY);
+            mPlugType = mBatteryManagerInternal.getPlugType();
+            mBatteryLevel = mBatteryManagerInternal.getBatteryLevel();
+            mBatteryLevelLow = mBatteryManagerInternal.getBatteryLevelLow();
 
             if (DEBUG_SPEW) {
                 Slog.d(TAG, "updateIsPoweredLocked: wasPowered=" + wasPowered
@@ -1254,7 +1244,7 @@
             final boolean wasStayOn = mStayOn;
             if (mStayOnWhilePluggedInSetting != 0
                     && !isMaximumScreenOffTimeoutFromDeviceAdminEnforcedLocked()) {
-                mStayOn = mBatteryService.isPowered(mStayOnWhilePluggedInSetting);
+                mStayOn = mBatteryManagerInternal.isPowered(mStayOnWhilePluggedInSetting);
             } else {
                 mStayOn = false;
             }
@@ -3076,10 +3066,5 @@
                 mLowPowerModeListeners.add(listener);
             }
         }
-
-        @Override
-        public void setPolicy(WindowManagerPolicy policy) {
-            PowerManagerService.this.setPolicy(policy);
-        }
     }
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 8d905ba..dc44e51 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -23,7 +23,4 @@
 
 public interface StatusBarManagerInternal {
     void setNotificationDelegate(NotificationDelegate delegate);
-    void addNotification(StatusBarNotification notification);
-    void updateNotification(StatusBarNotification notification);
-    void removeNotification(String key);
 }
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 55b5e3b..e548fa5 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -60,8 +60,6 @@
     private NotificationDelegate mNotificationDelegate;
     private volatile IStatusBar mBar;
     private StatusBarIconList mIcons = new StatusBarIconList();
-    private HashMap<String,StatusBarNotification> mNotifications
-            = new HashMap<String,StatusBarNotification>();
 
     // for disabling the status bar
     private final ArrayList<DisableRecord> mDisableRecords = new ArrayList<DisableRecord>();
@@ -111,56 +109,7 @@
     private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() {
         @Override
         public void setNotificationDelegate(NotificationDelegate delegate) {
-            synchronized (mNotifications) {
-                mNotificationDelegate = delegate;
-            }
-        }
-
-        @Override
-        public void addNotification(StatusBarNotification notification) {
-            synchronized (mNotifications) {
-                mNotifications.put(notification.getKey(), notification);
-                if (mBar != null) {
-                    try {
-                        mBar.addNotification(notification);
-                    } catch (RemoteException ex) {
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void updateNotification(StatusBarNotification notification) {
-            synchronized (mNotifications) {
-                String key = notification.getKey();
-                if (!mNotifications.containsKey(key)) {
-                    throw new IllegalArgumentException("updateNotification key not found: " + key);
-                }
-                mNotifications.put(notification.getKey(), notification);
-                if (mBar != null) {
-                    try {
-                        mBar.updateNotification(notification);
-                    } catch (RemoteException ex) {
-                    }
-                }
-            }
-        }
-
-        @Override
-        public void removeNotification(String key) {
-            synchronized (mNotifications) {
-                final StatusBarNotification n = mNotifications.remove(key);
-                if (n == null) {
-                    Slog.e(TAG, "removeNotification key not found: " + key);
-                    return;
-                }
-                if (mBar != null) {
-                    try {
-                        mBar.removeNotification(key);
-                    } catch (RemoteException ex) {
-                    }
-                }
-            }
+            mNotificationDelegate = delegate;
         }
     };
 
@@ -511,7 +460,7 @@
     // ================================================================================
     @Override
     public void registerStatusBar(IStatusBar bar, StatusBarIconList iconList,
-            List<StatusBarNotification> notifications, int switches[], List<IBinder> binders) {
+            int switches[], List<IBinder> binders) {
         enforceStatusBarService();
 
         Slog.i(TAG, "registerStatusBar bar=" + bar);
@@ -519,11 +468,6 @@
         synchronized (mIcons) {
             iconList.copyFrom(mIcons);
         }
-        synchronized (mNotifications) {
-            for (StatusBarNotification sbn : mNotifications.values()) {
-                notifications.add(sbn);
-            }
-        }
         synchronized (mLock) {
             switches[0] = gatherDisableActionsLocked(mCurrentUserId);
             switches[1] = mSystemUiVisibility;
@@ -708,15 +652,6 @@
             mIcons.dump(pw);
         }
 
-        synchronized (mNotifications) {
-            int i=0;
-            pw.println("Notification list:");
-            for (Map.Entry<String,StatusBarNotification> e: mNotifications.entrySet()) {
-                pw.printf("  %2d: %s\n", i, e.getValue().toString());
-                i++;
-            }
-        }
-
         synchronized (mLock) {
             pw.println("  mDisabled=0x" + Integer.toHexString(mDisabled));
             final int N = mDisableRecords.size();
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index b9ef492..32546df 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -186,16 +186,12 @@
                 if (resolveInfo.serviceInfo == null) continue;
 
                 String packageName = resolveInfo.serviceInfo.packageName;
-                // STOPSHIP Reenable this check once the GMS Core prebuild library has the
-                // permission.
-                /*
                 if (pm.checkPermission(PERMISSION_PROVIDE_AGENT, packageName)
                         != PackageManager.PERMISSION_GRANTED) {
                     Log.w(TAG, "Skipping agent because package " + packageName
                             + " does not have permission " + PERMISSION_PROVIDE_AGENT + ".");
                     continue;
                 }
-                */
 
                 ComponentName name = getComponentName(resolveInfo);
                 if (!enabledAgents.contains(name)) continue;
diff --git a/services/core/java/com/android/server/tv/TvInputHal.java b/services/core/java/com/android/server/tv/TvInputHal.java
index 34168a8..23c0a4c 100644
--- a/services/core/java/com/android/server/tv/TvInputHal.java
+++ b/services/core/java/com/android/server/tv/TvInputHal.java
@@ -20,12 +20,19 @@
 import android.media.tv.TvStreamConfig;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.Message;
 import android.view.Surface;
+import android.util.Slog;
+
+import java.util.LinkedList;
+import java.util.Queue;
 
 /**
  * Provides access to the low-level TV input hardware abstraction layer.
  */
-final class TvInputHal {
+final class TvInputHal implements Handler.Callback {
+    private final static String TAG = TvInputHal.class.getSimpleName();
+
     public final static int SUCCESS = 0;
     public final static int ERROR_NO_INIT = -1;
     public final static int ERROR_STALE_CONFIG = -2;
@@ -35,6 +42,12 @@
     public static final int TYPE_BUILT_IN_TUNER = 2;
     public static final int TYPE_PASSTHROUGH = 3;
 
+    public static final int EVENT_OPEN = 0;
+    // Below should be in sync with hardware/libhardware/include/hardware/tv_input.h
+    public static final int EVENT_DEVICE_AVAILABLE = 1;
+    public static final int EVENT_DEVICE_UNAVAILABLE = 2;
+    public static final int EVENT_STREAM_CONFIGURATION_CHANGED = 3;
+
     public interface Callback {
         public void onDeviceAvailable(
                 TvInputHardwareInfo info, TvStreamConfig[] configs);
@@ -50,7 +63,7 @@
             int generation);
     private static native void nativeClose(long ptr);
 
-    private long mPtr = 0l;
+    private volatile long mPtr = 0;
     private final Callback mCallback;
     private final HandlerThread mThread = new HandlerThread("TV input HAL event thread");
     private final Handler mHandler;
@@ -60,21 +73,23 @@
     public TvInputHal(Callback callback) {
         mCallback = callback;
         mThread.start();
-        mHandler = new Handler(mThread.getLooper());
+        mHandler = new Handler(mThread.getLooper(), this);
     }
 
     public void init() {
         mPtr = nativeOpen();
+        mHandler.sendEmptyMessage(EVENT_OPEN);
     }
 
     public int setSurface(int deviceId, Surface surface, TvStreamConfig streamConfig) {
-        if (mPtr == 0) {
+        long ptr = mPtr;
+        if (ptr == 0) {
             return ERROR_NO_INIT;
         }
         if (mStreamConfigGeneration != streamConfig.getGeneration()) {
             return ERROR_STALE_CONFIG;
         }
-        if (nativeSetSurface(mPtr, deviceId, streamConfig.getStreamId(), surface) == 0) {
+        if (nativeSetSurface(ptr, deviceId, streamConfig.getStreamId(), surface) == 0) {
             return SUCCESS;
         } else {
             return ERROR_UNKNOWN;
@@ -82,47 +97,81 @@
     }
 
     public void close() {
-        if (mPtr != 0l) {
-            nativeClose(mPtr);
+        long ptr = mPtr;
+        if (ptr != 0l) {
+            nativeClose(ptr);
             mThread.quitSafely();
         }
     }
 
-    private synchronized void retrieveStreamConfigs(int deviceId) {
+    private synchronized void retrieveStreamConfigs(long ptr, int deviceId) {
         ++mStreamConfigGeneration;
-        mStreamConfigs = nativeGetStreamConfigs(mPtr, deviceId, mStreamConfigGeneration);
+        mStreamConfigs = nativeGetStreamConfigs(ptr, deviceId, mStreamConfigGeneration);
     }
 
     // Called from native
-    private void deviceAvailableFromNative(int deviceId, int type) {
-        final TvInputHardwareInfo info = new TvInputHardwareInfo(deviceId, type);
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                retrieveStreamConfigs(info.getDeviceId());
-                mCallback.onDeviceAvailable(info, mStreamConfigs);
-            }
-        });
+    private void deviceAvailableFromNative(TvInputHardwareInfo info) {
+        mHandler.sendMessage(
+                mHandler.obtainMessage(EVENT_DEVICE_AVAILABLE, info));
     }
 
     private void deviceUnavailableFromNative(int deviceId) {
-        final int id = deviceId;
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                mCallback.onDeviceUnavailable(id);
-            }
-        });
+        mHandler.sendMessage(
+                mHandler.obtainMessage(EVENT_DEVICE_UNAVAILABLE, deviceId, 0));
     }
 
     private void streamConfigsChangedFromNative(int deviceId) {
-        final int id = deviceId;
-        mHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                retrieveStreamConfigs(id);
-                mCallback.onStreamConfigurationChanged(id, mStreamConfigs);
+        mHandler.sendMessage(
+                mHandler.obtainMessage(EVENT_STREAM_CONFIGURATION_CHANGED, deviceId, 0));
+    }
+
+    // Handler.Callback implementation
+
+    private Queue<Message> mPendingMessageQueue = new LinkedList<Message>();
+
+    @Override
+    public boolean handleMessage(Message msg) {
+        long ptr = mPtr;
+        if (ptr == 0) {
+            mPendingMessageQueue.add(msg);
+            return true;
+        }
+        while (!mPendingMessageQueue.isEmpty()) {
+            handleMessageInternal(ptr, mPendingMessageQueue.remove());
+        }
+        handleMessageInternal(ptr, msg);
+        return true;
+    }
+
+    private void handleMessageInternal(long ptr, Message msg) {
+        switch (msg.what) {
+            case EVENT_OPEN:
+                // No-op
+                break;
+
+            case EVENT_DEVICE_AVAILABLE: {
+                TvInputHardwareInfo info = (TvInputHardwareInfo)msg.obj;
+                retrieveStreamConfigs(ptr, info.getDeviceId());
+                mCallback.onDeviceAvailable(info, mStreamConfigs);
+                break;
             }
-        });
+
+            case EVENT_DEVICE_UNAVAILABLE: {
+                int deviceId = msg.arg1;
+                mCallback.onDeviceUnavailable(deviceId);
+                break;
+            }
+
+            case EVENT_STREAM_CONFIGURATION_CHANGED: {
+                int deviceId = msg.arg1;
+                retrieveStreamConfigs(ptr, deviceId);
+                mCallback.onStreamConfigurationChanged(deviceId, mStreamConfigs);
+                break;
+            }
+
+            default:
+                Slog.e(TAG, "Unknown event: " + msg);
+                break;
+        }
     }
 }
diff --git a/services/core/java/com/android/server/tv/TvInputHardwareManager.java b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
index e34f42b..1146f0f 100644
--- a/services/core/java/com/android/server/tv/TvInputHardwareManager.java
+++ b/services/core/java/com/android/server/tv/TvInputHardwareManager.java
@@ -17,6 +17,11 @@
 package com.android.server.tv;
 
 import android.content.Context;
+import android.media.AudioDevicePort;
+import android.media.AudioManager;
+import android.media.AudioPatch;
+import android.media.AudioPort;
+import android.media.AudioPortConfig;
 import android.media.tv.ITvInputHardware;
 import android.media.tv.ITvInputHardwareCallback;
 import android.media.tv.TvInputHardwareInfo;
@@ -48,11 +53,13 @@
     private final List<TvInputHardwareInfo> mInfoList = new ArrayList<TvInputHardwareInfo>();
     private final Context mContext;
     private final Set<Integer> mActiveHdmiSources = new HashSet<Integer>();
+    private final AudioManager mAudioManager;
 
     private final Object mLock = new Object();
 
     public TvInputHardwareManager(Context context) {
         mContext = context;
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
         // TODO(hdmi): mHdmiManager = mContext.getSystemService(...);
         // TODO(hdmi): mHdmiClient = mHdmiManager.getTvClient();
         mHal.init();
@@ -116,6 +123,19 @@
         }
     }
 
+    private boolean checkUidChangedLocked(
+            Connection connection, int callingUid, int resolvedUserId) {
+        Integer connectionCallingUid = connection.getCallingUidLocked();
+        Integer connectionResolvedUserId = connection.getResolvedUserIdLocked();
+        if (connectionCallingUid == null || connectionResolvedUserId == null) {
+            return true;
+        }
+        if (connectionCallingUid != callingUid || connectionResolvedUserId != resolvedUserId) {
+            return true;
+        }
+        return false;
+    }
+
     /**
      * Create a TvInputHardware object with a specific deviceId. One service at a time can access
      * the object, and if more than one process attempts to create hardware with the same deviceId,
@@ -133,8 +153,7 @@
                 Slog.e(TAG, "Invalid deviceId : " + deviceId);
                 return null;
             }
-            if (connection.getCallingUidLocked() != callingUid
-                    || connection.getResolvedUserIdLocked() != resolvedUserId) {
+            if (checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
                 TvInputHardwareImpl hardware = new TvInputHardwareImpl(connection.getInfoLocked());
                 try {
                     callback.asBinder().linkToDeath(connection, 0);
@@ -160,8 +179,7 @@
                 return;
             }
             if (connection.getHardwareLocked() != hardware
-                    || connection.getCallingUidLocked() != callingUid
-                    || connection.getResolvedUserIdLocked() != resolvedUserId) {
+                    || checkUidChangedLocked(connection, callingUid, resolvedUserId)) {
                 return;
             }
             connection.resetLocked(null, null, null, null);
@@ -226,11 +244,11 @@
             return mConfigs;
         }
 
-        public int getCallingUidLocked() {
+        public Integer getCallingUidLocked() {
             return mCallingUid;
         }
 
-        public int getResolvedUserIdLocked() {
+        public Integer getResolvedUserIdLocked() {
             return mResolvedUserId;
         }
 
@@ -247,12 +265,48 @@
         private boolean mReleased = false;
         private final Object mImplLock = new Object();
 
+        private final AudioDevicePort mAudioSource;
+        private final AudioDevicePort mAudioSink;
+        private AudioPatch mAudioPatch = null;
+
         public TvInputHardwareImpl(TvInputHardwareInfo info) {
             mInfo = info;
+            AudioDevicePort audioSource = null;
+            AudioDevicePort audioSink = null;
+            if (mInfo.getAudioType() != AudioManager.DEVICE_NONE) {
+                ArrayList<AudioPort> devicePorts = new ArrayList<AudioPort>();
+                if (mAudioManager.listAudioDevicePorts(devicePorts) == AudioManager.SUCCESS) {
+                    // Find source
+                    for (AudioPort port : devicePorts) {
+                        AudioDevicePort devicePort = (AudioDevicePort) port;
+                        if (devicePort.type() == mInfo.getAudioType() &&
+                                devicePort.address().equals(mInfo.getAudioAddress())) {
+                            audioSource = devicePort;
+                            break;
+                        }
+                    }
+                    // Find sink
+                    // TODO: App may want to specify sink device?
+                    int sinkDevices = mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC);
+                    for (AudioPort port : devicePorts) {
+                        AudioDevicePort devicePort = (AudioDevicePort) port;
+                        if (devicePort.type() == sinkDevices) {
+                            audioSink = devicePort;
+                            break;
+                        }
+                    }
+                }
+            }
+            mAudioSource = audioSource;
+            mAudioSink = audioSink;
         }
 
         public void release() {
             synchronized (mImplLock) {
+                if (mAudioPatch != null) {
+                    mAudioManager.releaseAudioPatch(mAudioPatch);
+                    mAudioPatch = null;
+                }
                 mReleased = true;
             }
         }
@@ -277,6 +331,22 @@
                         }
                     }
                 }
+                if (mAudioSource != null && mAudioSink != null) {
+                    if (surface != null) {
+                        AudioPortConfig sourceConfig = mAudioSource.activeConfig();
+                        AudioPortConfig sinkConfig = mAudioSink.activeConfig();
+                        AudioPatch[] audioPatchArray = new AudioPatch[] { mAudioPatch };
+                        // TODO: build config if activeConfig() == null
+                        mAudioManager.createAudioPatch(
+                                audioPatchArray,
+                                new AudioPortConfig[] { sourceConfig },
+                                new AudioPortConfig[] { sinkConfig });
+                        mAudioPatch = audioPatchArray[0];
+                    } else {
+                        mAudioManager.releaseAudioPatch(mAudioPatch);
+                        mAudioPatch = null;
+                    }
+                }
                 return mHal.setSurface(mInfo.getDeviceId(), surface, config) == TvInputHal.SUCCESS;
             }
         }
@@ -288,7 +358,7 @@
                     throw new IllegalStateException("Device already released.");
                 }
             }
-            // TODO
+            // TODO: Use AudioGain?
         }
 
         @Override
diff --git a/services/core/java/com/android/server/tv/TvInputManagerService.java b/services/core/java/com/android/server/tv/TvInputManagerService.java
index 10a67c4..6798f3f 100644
--- a/services/core/java/com/android/server/tv/TvInputManagerService.java
+++ b/services/core/java/com/android/server/tv/TvInputManagerService.java
@@ -19,12 +19,15 @@
 import android.app.ActivityManager;
 import android.content.BroadcastReceiver;
 import android.content.ComponentName;
+import android.content.ContentProviderOperation;
+import android.content.ContentProviderResult;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.OperationApplicationException;
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -66,11 +69,12 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 import java.io.IOException;
-
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /** This class provides a system service that manages television inputs. */
 public final class TvInputManagerService extends SystemService {
@@ -102,11 +106,9 @@
         mLogHandler = new LogHandler(IoThread.get().getLooper());
 
         mTvInputHardwareManager = new TvInputHardwareManager(context);
-        registerBroadcastReceivers();
 
         synchronized (mLock) {
             mUserStates.put(mCurrentUserId, new UserState());
-            buildTvInputListLocked(mCurrentUserId);
         }
     }
 
@@ -115,6 +117,16 @@
         publishBinderService(Context.TV_INPUT_SERVICE, new BinderService());
     }
 
+    @Override
+    public void onBootPhase(int phase) {
+        if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
+            registerBroadcastReceivers();
+            synchronized (mLock) {
+                buildTvInputListLocked(mCurrentUserId);
+            }
+        }
+    }
+
     private void registerBroadcastReceivers() {
         PackageMonitor monitor = new PackageMonitor() {
             @Override
@@ -123,6 +135,44 @@
                     buildTvInputListLocked(mCurrentUserId);
                 }
             }
+
+            @Override
+            public void onPackageRemoved(String packageName, int uid) {
+                synchronized (mLock) {
+                    UserState userState = getUserStateLocked(mCurrentUserId);
+                    if (!userState.packageList.contains(packageName)) {
+                        // Not a TV input package.
+                        return;
+                    }
+                }
+
+                ArrayList<ContentProviderOperation> operations =
+                        new ArrayList<ContentProviderOperation>();
+
+                String selection = TvContract.BaseTvColumns.COLUMN_PACKAGE_NAME + "=?";
+                String[] selectionArgs = { packageName };
+
+                operations.add(ContentProviderOperation.newDelete(TvContract.Channels.CONTENT_URI)
+                        .withSelection(selection, selectionArgs).build());
+                operations.add(ContentProviderOperation.newDelete(TvContract.Programs.CONTENT_URI)
+                        .withSelection(selection, selectionArgs).build());
+                operations.add(ContentProviderOperation
+                        .newDelete(TvContract.WatchedPrograms.CONTENT_URI)
+                        .withSelection(selection, selectionArgs).build());
+
+                ContentProviderResult[] results = null;
+                try {
+                    results = mContentResolver.applyBatch(TvContract.AUTHORITY, operations);
+                } catch (RemoteException | OperationApplicationException e) {
+                    Slog.e(TAG, "error in applyBatch" + e);
+                }
+
+                if (DEBUG) {
+                    Slog.d(TAG, "onPackageRemoved(packageName=" + packageName + ", uid=" + uid
+                            + ")");
+                    Slog.d(TAG, "results=" + results);
+                }
+            }
         };
         monitor.register(mContext, null, UserHandle.ALL, true);
 
@@ -145,6 +195,7 @@
     private void buildTvInputListLocked(int userId) {
         UserState userState = getUserStateLocked(userId);
         userState.inputMap.clear();
+        userState.packageList.clear();
 
         if (DEBUG) Slog.d(TAG, "buildTvInputList");
         PackageManager pm = mContext.getPackageManager();
@@ -162,6 +213,7 @@
                 TvInputInfo info = TvInputInfo.createTvInputInfo(mContext, ri);
                 if (DEBUG) Slog.d(TAG, "add " + info.getId());
                 userState.inputMap.put(info.getId(), info);
+                userState.packageList.add(si.packageName);
             } catch (IOException | XmlPullParserException e) {
                 Slog.e(TAG, "Can't load TV input " + si.name, e);
             }
@@ -348,7 +400,7 @@
                     if (session == null) {
                         removeSessionStateLocked(sessionToken, userId);
                         sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
-                                null, null, sessionState.mSeq, userId);
+                                null, null, sessionState.mSeq);
                     } else {
                         try {
                             session.asBinder().linkToDeath(sessionState, 0);
@@ -364,7 +416,7 @@
                         clientState.mSessionTokens.add(sessionState.mSessionToken);
 
                         sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId,
-                                sessionToken, channels[0], sessionState.mSeq, userId);
+                                sessionToken, channels[0], sessionState.mSeq);
                     }
                     channels[0].dispose();
                 }
@@ -449,13 +501,13 @@
             Slog.e(TAG, "error in createSession", e);
             removeSessionStateLocked(sessionToken, userId);
             sendSessionTokenToClientLocked(sessionState.mClient, sessionState.mInputId, null, null,
-                    sessionState.mSeq, userId);
+                    sessionState.mSeq);
         }
         channels[1].dispose();
     }
 
     private void sendSessionTokenToClientLocked(ITvInputClient client, String inputId,
-            IBinder sessionToken, InputChannel channel, int seq, int userId) {
+            IBinder sessionToken, InputChannel channel, int seq) {
         try {
             client.onSessionCreated(inputId, sessionToken, channel, seq);
         } catch (RemoteException exception) {
@@ -587,7 +639,9 @@
             } finally {
                 Binder.restoreCallingIdentity(identity);
             }
-            return false;
+            // STOPSHIP: Redesign the API around the availability change. For now, the service
+            // will be always available.
+            return true;
         }
 
         @Override
@@ -672,7 +726,7 @@
                     }
                     // Send a null token immediately while reconnecting.
                     if (serviceState.mReconnecting == true) {
-                        sendSessionTokenToClientLocked(client, inputId, null, null, seq, userId);
+                        sendSessionTokenToClientLocked(client, inputId, null, null, seq);
                         return;
                     }
 
@@ -784,7 +838,10 @@
                         }
 
                         // Create a log entry and fill it later.
+                        String packageName = userState.inputMap.get(sessionState.mInputId)
+                                .getServiceInfo().packageName;
                         ContentValues values = new ContentValues();
+                        values.put(TvContract.WatchedPrograms.COLUMN_PACKAGE_NAME, packageName);
                         values.put(TvContract.WatchedPrograms.COLUMN_WATCH_START_TIME_UTC_MILLIS,
                                 currentTime);
                         values.put(TvContract.WatchedPrograms.COLUMN_WATCH_END_TIME_UTC_MILLIS, 0);
@@ -931,6 +988,9 @@
         // A mapping from the TV input id to its TvInputInfo.
         private final Map<String, TvInputInfo> inputMap = new HashMap<String,TvInputInfo>();
 
+        // A list of all TV input packages.
+        private final Set<String> packageList = new HashSet<String>();
+
         // A mapping from the token of a client to its state.
         private final Map<IBinder, ClientState> clientStateMap =
                 new HashMap<IBinder, ClientState>();
@@ -1095,8 +1155,7 @@
                         if (sessionState.mSession == null) {
                             removeSessionStateLocked(sessionToken, sessionState.mUserId);
                             sendSessionTokenToClientLocked(sessionState.mClient,
-                                    sessionState.mInputId, null, null, sessionState.mSeq,
-                                    sessionState.mUserId);
+                                    sessionState.mInputId, null, null, sessionState.mSeq);
                         }
                     }
 
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 6cb6b76..d05d0c7 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -961,8 +961,6 @@
                     // If the window is not touchable, do not report it but take into account
                     // the space it takes since the content behind it cannot be touched.
                     if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
-                        unaccountedSpace.op(boundsInScreen, unaccountedSpace,
-                                Region.Op.DIFFERENCE);
                         continue;
                     }
 
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 45326f7..f1d0585d 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -391,7 +391,7 @@
                     final WindowStateAnimator winAnimator = unForceHiding.get(i);
                     winAnimator.setAnimation(a);
                     winAnimator.mAnimationIsEntrance = true;
-                    if (startKeyguardExit) {
+                    if (startKeyguardExit && mKeyguardGoingAway) {
                         // Do one time only.
                         mPolicy.startKeyguardExitAnimation(mCurrentTime + a.getStartOffset(),
                                 a.getDuration());
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 2d8a34b..1d85723 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -411,7 +411,7 @@
     /**
      * And the callback to make when they've all been drawn.
      */
-    IRemoteCallback mWaitingForDrawnCallback;
+    Runnable mWaitingForDrawnCallback;
 
     /**
      * Windows that have called relayout() while we were running animations,
@@ -766,6 +766,8 @@
         mDisplaySettings = new DisplaySettings(context);
         mDisplaySettings.readSettingsLocked();
 
+        LocalServices.addService(WindowManagerPolicy.class, mPolicy);
+
         mPointerEventDispatcher = new PointerEventDispatcher(mInputManager.monitorInput(TAG));
 
         mFxSession = new SurfaceSession();
@@ -779,7 +781,6 @@
 
         mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
         mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
-        mPowerManagerInternal.setPolicy(mPolicy); // TODO: register as local service instead
         mPowerManagerInternal.registerLowPowerModeObserver(
                 new PowerManagerInternal.LowPowerModeListener() {
             @Override
@@ -7560,7 +7561,7 @@
                 }
 
                 case WAITING_FOR_DRAWN_TIMEOUT: {
-                    IRemoteCallback callback = null;
+                    Runnable callback = null;
                     synchronized (mWindowMap) {
                         Slog.w(TAG, "Timeout waiting for drawn: undrawn=" + mWaitingForDrawn);
                         mWaitingForDrawn.clear();
@@ -7568,10 +7569,7 @@
                         mWaitingForDrawnCallback = null;
                     }
                     if (callback != null) {
-                        try {
-                            callback.sendResult(null);
-                        } catch (RemoteException e) {
-                        }
+                        callback.run();
                     }
                     break;
                 }
@@ -7625,16 +7623,13 @@
                     }
                     break;
                 case ALL_WINDOWS_DRAWN: {
-                    IRemoteCallback callback;
+                    Runnable callback;
                     synchronized (mWindowMap) {
                         callback = mWaitingForDrawnCallback;
                         mWaitingForDrawnCallback = null;
                     }
                     if (callback != null) {
-                        try {
-                            callback.sendResult(null);
-                        } catch (RemoteException e) {
-                        }
+                        callback.run();
                     }
                 }
                 case NEW_ANIMATOR_SCALE: {
@@ -9554,8 +9549,9 @@
             }
         }
 
-        if (mInnerFields.mOrientationChangeComplete && !defaultDisplay.layoutNeeded
-                && !mInnerFields.mUpdateRotation) {
+        if (mWaitingForDrawnCallback != null ||
+                (mInnerFields.mOrientationChangeComplete && !defaultDisplay.layoutNeeded &&
+                        !mInnerFields.mUpdateRotation)) {
             checkDrawnWindowsLocked();
         }
 
@@ -10320,6 +10316,10 @@
         mPolicy.lockNow(options);
     }
 
+    public void showRecentApps() {
+        mPolicy.showRecentApps();
+    }
+
     @Override
     public boolean isSafeModeEnabled() {
         return mSafeMode;
@@ -11140,24 +11140,31 @@
             }
         }
 
-        public void waitForAllWindowsDrawn(IRemoteCallback callback, long timeout) {
+        public void waitForAllWindowsDrawn(Runnable callback, long timeout) {
             synchronized (mWindowMap) {
                 mWaitingForDrawnCallback = callback;
                 final WindowList windows = getDefaultWindowListLocked();
                 for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {
                     final WindowState win = windows.get(winNdx);
-                    if (win.mHasSurface) {
-                        win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+                    if (win.mHasSurface && win.isWinVisibleLw()) {
+                        if (!win.mIsWallpaper) {
+                            // Don't force wallpaper to redraw.
+                            win.mWinAnimator.mDrawState = WindowStateAnimator.DRAW_PENDING;
+                        }
                         // Force add to mResizingWindows.
                         win.mLastContentInsets.set(-1, -1, -1, -1);
                         mWaitingForDrawn.add(win);
                     }
                 }
                 requestTraversalLocked();
-                mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
-                mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, timeout);
             }
-            checkDrawnWindowsLocked();
+            mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
+            if (mWaitingForDrawn.isEmpty()) {
+                callback.run();
+            } else {
+                mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, timeout);
+                checkDrawnWindowsLocked();
+            }
         }
     }
 }
diff --git a/services/core/jni/com_android_server_AssetAtlasService.cpp b/services/core/jni/com_android_server_AssetAtlasService.cpp
index 9a5079d..3696e24 100644
--- a/services/core/jni/com_android_server_AssetAtlasService.cpp
+++ b/services/core/jni/com_android_server_AssetAtlasService.cpp
@@ -61,8 +61,7 @@
         jobject canvas, jint width, jint height) {
 
     SkBitmap* bitmap = new SkBitmap;
-    bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
-    bitmap->allocPixels();
+    bitmap->allocN32Pixels(width, height);
     bitmap->eraseColor(0);
     INVOKEV(canvas, gCanvasClassInfo.setNativeBitmap, reinterpret_cast<jlong>(bitmap));
 
diff --git a/services/core/jni/com_android_server_tv_TvInputHal.cpp b/services/core/jni/com_android_server_tv_TvInputHal.cpp
index afe629d..7b8e6fd 100644
--- a/services/core/jni/com_android_server_tv_TvInputHal.cpp
+++ b/services/core/jni/com_android_server_tv_TvInputHal.cpp
@@ -54,6 +54,18 @@
     jmethodID build;
 } gTvStreamConfigBuilderClassInfo;
 
+static struct {
+    jclass clazz;
+
+    jmethodID constructor;
+    jmethodID deviceId;
+    jmethodID type;
+    jmethodID hdmiPortId;
+    jmethodID audioType;
+    jmethodID audioAddress;
+    jmethodID build;
+} gTvInputHardwareInfoBuilderClassInfo;
+
 ////////////////////////////////////////////////////////////////////////////////
 
 class JTvInputHal {
@@ -63,7 +75,6 @@
     static JTvInputHal* createInstance(JNIEnv* env, jobject thiz);
 
     int setSurface(int deviceId, int streamId, const sp<Surface>& surface);
-    void getStreamConfigs(int deviceId, jobjectArray* array);
     const tv_stream_config_t* getStreamConfigs(int deviceId, int* numConfigs);
 
 private:
@@ -209,7 +220,6 @@
     return configs;
 }
 
-
 // static
 void JTvInputHal::notify(
         tv_input_device_t* dev, tv_input_event_t* event, void* data) {
@@ -232,11 +242,36 @@
 void JTvInputHal::onDeviceAvailable(const tv_input_device_info_t& info) {
     JNIEnv* env = AndroidRuntime::getJNIEnv();
     mConnections.add(info.device_id, Connection());
+
+    jobject builder = env->NewObject(
+            gTvInputHardwareInfoBuilderClassInfo.clazz,
+            gTvInputHardwareInfoBuilderClassInfo.constructor);
+    env->CallObjectMethod(
+            builder, gTvInputHardwareInfoBuilderClassInfo.deviceId, info.device_id);
+    env->CallObjectMethod(
+            builder, gTvInputHardwareInfoBuilderClassInfo.type, info.type);
+    if (info.type == TV_INPUT_TYPE_HDMI) {
+        env->CallObjectMethod(
+                builder, gTvInputHardwareInfoBuilderClassInfo.hdmiPortId, info.hdmi.port_id);
+    }
+    env->CallObjectMethod(
+            builder, gTvInputHardwareInfoBuilderClassInfo.audioType, info.audio_type);
+    if (info.audio_type != AUDIO_DEVICE_NONE) {
+        jstring audioAddress = env->NewStringUTF(info.audio_address);
+        env->CallObjectMethod(
+                builder, gTvInputHardwareInfoBuilderClassInfo.audioAddress, audioAddress);
+        env->DeleteLocalRef(audioAddress);
+    }
+
+    jobject infoObject = env->CallObjectMethod(builder, gTvInputHardwareInfoBuilderClassInfo.build);
+
     env->CallVoidMethod(
             mThiz,
             gTvInputHalClassInfo.deviceAvailable,
-            info.device_id,
-            info.type);
+            infoObject);
+
+    env->DeleteLocalRef(builder);
+    env->DeleteLocalRef(infoObject);
 }
 
 void JTvInputHal::onDeviceUnavailable(int deviceId) {
@@ -339,7 +374,8 @@
     FIND_CLASS(clazz, "com/android/server/tv/TvInputHal");
 
     GET_METHOD_ID(
-            gTvInputHalClassInfo.deviceAvailable, clazz, "deviceAvailableFromNative", "(II)V");
+            gTvInputHalClassInfo.deviceAvailable, clazz,
+            "deviceAvailableFromNative", "(Landroid/media/tv/TvInputHardwareInfo;)V");
     GET_METHOD_ID(
             gTvInputHalClassInfo.deviceUnavailable, clazz, "deviceUnavailableFromNative", "(I)V");
     GET_METHOD_ID(
@@ -382,6 +418,40 @@
             gTvStreamConfigBuilderClassInfo.clazz,
             "build", "()Landroid/media/tv/TvStreamConfig;");
 
+    FIND_CLASS(gTvInputHardwareInfoBuilderClassInfo.clazz,
+            "android/media/tv/TvInputHardwareInfo$Builder");
+    gTvInputHardwareInfoBuilderClassInfo.clazz =
+            jclass(env->NewGlobalRef(gTvInputHardwareInfoBuilderClassInfo.clazz));
+
+    GET_METHOD_ID(
+            gTvInputHardwareInfoBuilderClassInfo.constructor,
+            gTvInputHardwareInfoBuilderClassInfo.clazz,
+            "<init>", "()V");
+    GET_METHOD_ID(
+            gTvInputHardwareInfoBuilderClassInfo.deviceId,
+            gTvInputHardwareInfoBuilderClassInfo.clazz,
+            "deviceId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
+    GET_METHOD_ID(
+            gTvInputHardwareInfoBuilderClassInfo.type,
+            gTvInputHardwareInfoBuilderClassInfo.clazz,
+            "type", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
+    GET_METHOD_ID(
+            gTvInputHardwareInfoBuilderClassInfo.hdmiPortId,
+            gTvInputHardwareInfoBuilderClassInfo.clazz,
+            "hdmiPortId", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
+    GET_METHOD_ID(
+            gTvInputHardwareInfoBuilderClassInfo.audioType,
+            gTvInputHardwareInfoBuilderClassInfo.clazz,
+            "audioType", "(I)Landroid/media/tv/TvInputHardwareInfo$Builder;");
+    GET_METHOD_ID(
+            gTvInputHardwareInfoBuilderClassInfo.audioAddress,
+            gTvInputHardwareInfoBuilderClassInfo.clazz,
+            "audioAddress", "(Ljava/lang/String;)Landroid/media/tv/TvInputHardwareInfo$Builder;");
+    GET_METHOD_ID(
+            gTvInputHardwareInfoBuilderClassInfo.build,
+            gTvInputHardwareInfoBuilderClassInfo.clazz,
+            "build", "()Landroid/media/tv/TvInputHardwareInfo;");
+
     return 0;
 }
 
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 24a998d..9c38bbc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -195,7 +195,7 @@
                 = new ArrayList<ActiveAdmin>();
 
         // This is the list of component allowed to start lock task mode.
-        final List<ComponentName> mLockTaskComponents = new ArrayList<ComponentName>();
+        final List<String> mLockTaskPackages = new ArrayList<String>();
 
         ComponentName mRestrictionsProvider;
 
@@ -1014,10 +1014,10 @@
                 out.endTag(null, "active-password");
             }
 
-            for (int i=0; i<policy.mLockTaskComponents.size(); i++) {
-                ComponentName component = policy.mLockTaskComponents.get(i);
+            for (int i=0; i<policy.mLockTaskPackages.size(); i++) {
+                String component = policy.mLockTaskPackages.get(i);
                 out.startTag(null, LOCK_TASK_COMPONENTS_XML);
-                out.attribute(null, "name", component.flattenToString());
+                out.attribute(null, "name", component);
                 out.endTag(null, LOCK_TASK_COMPONENTS_XML);
             }
 
@@ -1077,7 +1077,7 @@
 
             type = parser.next();
             int outerDepth = parser.getDepth();
-            policy.mLockTaskComponents.clear();
+            policy.mLockTaskPackages.clear();
             while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
                    && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
@@ -1131,9 +1131,7 @@
                             parser.getAttributeValue(null, "nonletter"));
                     XmlUtils.skipCurrentTag(parser);
                 } else if (LOCK_TASK_COMPONENTS_XML.equals(tag)) {
-                    policy.mLockTaskComponents.add
-                        (ComponentName.unflattenFromString
-                         (parser.getAttributeValue(null, "name")));
+                    policy.mLockTaskPackages.add(parser.getAttributeValue(null, "name"));
                     XmlUtils.skipCurrentTag(parser);
                 } else {
                     Slog.w(LOG_TAG, "Unknown tag: " + tag);
@@ -3463,12 +3461,12 @@
             long id = Binder.clearCallingIdentity();
             try {
                 if ((flags & DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED) != 0) {
-                    pm.addCrossProfileIntentFilter(filter, true /*removable*/, callingUserId,
-                            UserHandle.USER_OWNER);
+                    pm.addCrossProfileIntentFilter(filter, callingUserId, UserHandle.USER_OWNER,
+                            PackageManager.SET_BY_PROFILE_OWNER);
                 }
                 if ((flags & DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT) != 0) {
-                    pm.addCrossProfileIntentFilter(filter, true /*removable*/, UserHandle.USER_OWNER,
-                            callingUserId);
+                    pm.addCrossProfileIntentFilter(filter, UserHandle.USER_OWNER, callingUserId,
+                            PackageManager.SET_BY_PROFILE_OWNER);
                 }
             } catch (RemoteException re) {
                 // Shouldn't happen
@@ -3489,6 +3487,8 @@
             long id = Binder.clearCallingIdentity();
             try {
                 pm.clearCrossProfileIntentFilters(callingUserId);
+                // If we want to support multiple managed profiles, we will have to only remove
+                // those that have callingUserId as their target.
                 pm.clearCrossProfileIntentFilters(UserHandle.USER_OWNER);
             } catch (RemoteException re) {
                 // Shouldn't happen
@@ -3730,38 +3730,38 @@
     }
 
     /**
-     * Sets which componets may enter lock task mode.
+     * Sets which packages may enter lock task mode.
      *
-     * This function can only be called by the device owner or the profile owner.
+     * This function can only be called by the device owner.
      * @param components The list of components allowed to enter lock task mode.
      */
-    public void setLockTaskComponents(ComponentName[] components) throws SecurityException {
+    public void setLockTaskPackages(String[] packages) throws SecurityException {
         // Get the package names of the caller.
         int uid = Binder.getCallingUid();
         String[] packageNames = mContext.getPackageManager().getPackagesForUid(uid);
 
-        // Check whether any of the package name is the device owner or the profile owner.
-        for (int i=0; i<packageNames.length; i++) {
-            String packageName = packageNames[i];
-            int userHandle = UserHandle.getUserId(uid);
-            String profileOwnerPackage = getProfileOwner(userHandle);
-            if (isDeviceOwner(packageName) ||
-                (profileOwnerPackage != null && profileOwnerPackage.equals(packageName))) {
+        synchronized (this) {
+            // Check whether any of the package name is the device owner.
+            for (int i=0; i<packageNames.length; i++) {
+                String packageName = packageNames[i];
+                int userHandle = UserHandle.getUserId(uid);
+                if (isDeviceOwner(packageName)) {
 
-                // If a package name is the device owner or the profile owner,
-                // we update the component list.
-                DevicePolicyData policy = getUserData(userHandle);
-                policy.mLockTaskComponents.clear();
-                if (components != null) {
-                    for (int j=0; j<components.length; j++) {
-                        ComponentName component = components[j];
-                        policy.mLockTaskComponents.add(component);
+                    // If a package name is the device owner,
+                    // we update the component list.
+                    DevicePolicyData policy = getUserData(userHandle);
+                    policy.mLockTaskPackages.clear();
+                    if (packages != null) {
+                        for (int j = 0; j < packages.length; j++) {
+                            String pkg = packages[j];
+                            policy.mLockTaskPackages.add(pkg);
+                        }
                     }
-                }
 
-                // Store the settings persistently.
-                saveSettingsLocked(userHandle);
-                return;
+                    // Store the settings persistently.
+                    saveSettingsLocked(userHandle);
+                    return;
+                }
             }
         }
         throw new SecurityException();
@@ -3770,36 +3770,61 @@
     /**
      * This function returns the list of components allowed to start the task lock mode.
      */
-    public ComponentName[] getLockTaskComponents() {
-        int userHandle = UserHandle.USER_OWNER;
-        DevicePolicyData policy = getUserData(userHandle);
-        ComponentName[] tempArray = policy.mLockTaskComponents.toArray(new ComponentName[0]);
-        return tempArray;
+    public String[] getLockTaskPackages() {
+        synchronized (this) {
+            int userHandle = UserHandle.USER_OWNER;
+            DevicePolicyData policy = getUserData(userHandle);
+            return policy.mLockTaskPackages.toArray(new String[0]);
+        }
     }
 
     /**
-     * This function lets the caller know whether the given component is allowed to start the
+     * This function lets the caller know whether the given package is allowed to start the
      * lock task mode.
-     * @param component The component to check
+     * @param pkg The package to check
      */
-    public boolean isLockTaskPermitted(ComponentName component) {
+    public boolean isLockTaskPermitted(String pkg) {
         // Get current user's devicepolicy
         int uid = Binder.getCallingUid();
         int userHandle = UserHandle.getUserId(uid);
         DevicePolicyData policy = getUserData(userHandle);
-        for (int i=0; i<policy.mLockTaskComponents.size(); i++) {
-            ComponentName lockTaskComponent = policy.mLockTaskComponents.get(i);
+        synchronized (this) {
+            for (int i = 0; i < policy.mLockTaskPackages.size(); i++) {
+                String lockTaskPackage = policy.mLockTaskPackages.get(i);
 
-            // If the given component equals one of the component stored our device-owner-set
-            // list, we allow this component to start the lock task mode.
-            if (lockTaskComponent.getPackageName().equals(component.getPackageName())) {
-                return true;
+                // If the given package equals one of the packages stored our list,
+                // we allow this package to start lock task mode.
+                if (lockTaskPackage.equals(pkg)) {
+                    return true;
+                }
             }
         }
         return false;
     }
 
     @Override
+    public void notifyLockTaskModeChanged(boolean isEnabled, String pkg, int userHandle) {
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("notifyLockTaskModeChanged can only be called by system");
+        }
+        synchronized (this) {
+            final DevicePolicyData policy = getUserData(userHandle);
+            Bundle adminExtras = new Bundle();
+            adminExtras.putBoolean(DeviceAdminReceiver.EXTRA_LOCK_TASK_ENTERING, isEnabled);
+            adminExtras.putString(DeviceAdminReceiver.EXTRA_LOCK_TASK_PACKAGE, pkg);
+            for (ActiveAdmin admin : policy.mAdminList) {
+                boolean ownsDevice = isDeviceOwner(admin.info.getPackageName());
+                boolean ownsProfile = (getProfileOwner(userHandle) != null
+                        && getProfileOwner(userHandle).equals(admin.info.getPackageName()));
+                if (ownsDevice || ownsProfile) {
+                    sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_LOCK_TASK_CHANGED,
+                            adminExtras, null);
+                }
+            }
+        }
+    }
+
+    @Override
     public void setGlobalSetting(ComponentName who, String setting, String value) {
         final ContentResolver contentResolver = mContext.getContentResolver();
 
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5cccdd6..0d05c5f 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -44,7 +44,6 @@
 import android.os.SystemProperties;
 import android.os.UserHandle;
 import android.service.dreams.DreamService;
-import android.service.fingerprint.FingerprintManager;
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
@@ -148,8 +147,13 @@
     private PowerManagerService mPowerManagerService;
     private ActivityManagerService mActivityManagerService;
     private DisplayManagerService mDisplayManagerService;
+    private PackageManagerService mPackageManagerService;
+    private PackageManager mPackageManager;
     private ContentResolver mContentResolver;
 
+    private boolean mOnlyCore;
+    private boolean mFirstBoot;
+
     /**
      * Called to initialize native system services.
      */
@@ -163,6 +167,7 @@
     }
 
     public SystemServer() {
+        // Check for factory test mode.
         mFactoryTestMode = FactoryTest.getMode();
     }
 
@@ -245,7 +250,7 @@
             startBootstrapServices();
             startCoreServices();
             startOtherServices();
-        } catch (RuntimeException ex) {
+        } catch (Throwable ex) {
             Slog.e("System", "******************************************");
             Slog.e("System", "************ Failure starting system services", ex);
             throw ex;
@@ -289,36 +294,84 @@
         mSystemContext.setTheme(android.R.style.Theme_DeviceDefault_Light_DarkActionBar);
     }
 
+    /**
+     * Starts the small tangle of critical services that are needed to get
+     * the system off the ground.  These services have complex mutual dependencies
+     * which is why we initialize them all in one place here.  Unless your service
+     * is also entwined in these dependencies, it should be initialized in one of
+     * the other functions.
+     */
     private void startBootstrapServices() {
         // Wait for installd to finish starting up so that it has a chance to
         // create critical directories such as /data/user with the appropriate
         // permissions.  We need this to complete before we initialize other services.
         mInstaller = mSystemServiceManager.startService(Installer.class);
 
-        // Power manager needs to be started early because other services need it.
-        // TODO: The conversion to the new pattern is incomplete.  We need to switch
-        // the power manager's dependencies over then we can use boot phases to arrange
-        // initialization order and remove the mPowerManagerService field.
-        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
-
         // Activity manager runs the show.
         mActivityManagerService = mSystemServiceManager.startService(
                 ActivityManagerService.Lifecycle.class).getService();
         mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
-    }
 
-    private void startCoreServices() {
+        // Power manager needs to be started early because other services need it.
+        // Native daemons may be watching for it to be registered so it must be ready
+        // to handle incoming binder calls immediately (including being able to verify
+        // the permissions for those calls).
+        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
+
+        // Now that the power manager has been started, let the activity manager
+        // initialize power management features.
+        mActivityManagerService.initPowerManagement();
+
         // Display manager is needed to provide display metrics before package manager
         // starts up.
         mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);
+
+        // We need the default display before we can initialize the package manager.
+        mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
+
+        // Only run "core" apps if we're encrypting the device.
+        String cryptState = SystemProperties.get("vold.decrypt");
+        if (ENCRYPTING_STATE.equals(cryptState)) {
+            Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
+            mOnlyCore = true;
+        } else if (ENCRYPTED_STATE.equals(cryptState)) {
+            Slog.w(TAG, "Device encrypted - only parsing core apps");
+            mOnlyCore = true;
+        }
+
+        // Start the package manager.
+        Slog.i(TAG, "Package Manager");
+        mPackageManagerService = PackageManagerService.main(mSystemContext, mInstaller,
+                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
+        mFirstBoot = mPackageManagerService.isFirstBoot();
+        mPackageManager = mSystemContext.getPackageManager();
+
+        // Initialize attribute cache used to cache resources from packages.
+        AttributeCache.init(mSystemContext);
+
+        // Set up the Application instance for the system process and get started.
+        mActivityManagerService.setSystemProcess();
     }
 
+    /**
+     * Starts some essential services that are not tangled up in the bootstrap process.
+     */
+    private void startCoreServices() {
+        // Manages LEDs and display backlight.
+        mSystemServiceManager.startService(LightsService.class);
+
+        // Tracks the battery level.  Requires LightService.
+        mSystemServiceManager.startService(BatteryService.class);
+    }
+
+    /**
+     * Starts a miscellaneous grab bag of stuff that has yet to be refactored
+     * and organized.
+     */
     private void startOtherServices() {
         final Context context = mSystemContext;
         AccountManagerService accountManager = null;
         ContentService contentService = null;
-        LightsManager lights = null;
-        BatteryService battery = null;
         VibratorService vibrator = null;
         IAlarmManager alarm = null;
         MountService mountService = null;
@@ -328,7 +381,6 @@
         ConnectivityService connectivity = null;
         NetworkScoreService networkScore = null;
         NsdService serviceDiscovery= null;
-        IPackageManager pm = null;
         WindowManagerService wm = null;
         BluetoothManagerService bluetooth = null;
         UsbService usb = null;
@@ -341,8 +393,6 @@
         ConsumerIrService consumerIr = null;
         AudioService audioService = null;
 
-        boolean onlyCore = false;
-        boolean firstBoot = false;
         boolean disableStorage = SystemProperties.getBoolean("config.disable_storage", false);
         boolean disableMedia = SystemProperties.getBoolean("config.disable_media", false);
         boolean disableBluetooth = SystemProperties.getBoolean("config.disable_bluetooth", false);
@@ -354,38 +404,12 @@
         boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
 
         try {
-            Slog.i(TAG, "Telephony Registry");
-            telephonyRegistry = new TelephonyRegistry(context);
-            ServiceManager.addService("telephony.registry", telephonyRegistry);
-
             Slog.i(TAG, "Scheduling Policy");
             ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());
 
-            AttributeCache.init(context);
-
-            // We need the default display before we can initialize the package manager.
-            mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
-
-            Slog.i(TAG, "Package Manager");
-            // Only run "core" apps if we're encrypting the device.
-            String cryptState = SystemProperties.get("vold.decrypt");
-            if (ENCRYPTING_STATE.equals(cryptState)) {
-                Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
-                onlyCore = true;
-            } else if (ENCRYPTED_STATE.equals(cryptState)) {
-                Slog.w(TAG, "Device encrypted - only parsing core apps");
-                onlyCore = true;
-            }
-
-            pm = PackageManagerService.main(context, mInstaller,
-                    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF,
-                    onlyCore);
-            try {
-                firstBoot = pm.isFirstBoot();
-            } catch (RemoteException e) {
-            }
-
-            mActivityManagerService.setSystemProcess();
+            Slog.i(TAG, "Telephony Registry");
+            telephonyRegistry = new TelephonyRegistry(context);
+            ServiceManager.addService("telephony.registry", telephonyRegistry);
 
             Slog.i(TAG, "Entropy Mixer");
             ServiceManager.addService("entropy", new EntropyMixer(context));
@@ -413,24 +437,10 @@
             Slog.i(TAG, "System Content Providers");
             mActivityManagerService.installSystemProviders();
 
-            mSystemServiceManager.startService(LightsService.class);
-            lights = LocalServices.getService(LightsManager.class);
-
-            Slog.i(TAG, "Battery Service");
-            battery = new BatteryService(context, lights);
-            ServiceManager.addService("battery", battery);
-
             Slog.i(TAG, "Vibrator Service");
             vibrator = new VibratorService(context);
             ServiceManager.addService("vibrator", vibrator);
 
-            // TODO: use boot phase
-            // only initialize the power service after we have started the
-            // lights service, content providers and the battery service.
-            mPowerManagerService.init(lights, battery,
-                    BatteryStatsService.getService(),
-                    mActivityManagerService.getAppOpsService());
-
             Slog.i(TAG, "Consumer IR Service");
             consumerIr = new ConsumerIrService(context);
             ServiceManager.addService(Context.CONSUMER_IR_SERVICE, consumerIr);
@@ -449,7 +459,7 @@
             Slog.i(TAG, "Window Manager");
             wm = WindowManagerService.main(context, inputManager,
                     mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
-                    !firstBoot, onlyCore);
+                    !mFirstBoot, mOnlyCore);
             ServiceManager.addService(Context.WINDOW_SERVICE, wm);
             ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
 
@@ -523,7 +533,7 @@
         }
 
         try {
-            pm.performBootDexOpt();
+            mPackageManagerService.performBootDexOpt();
         } catch (Throwable e) {
             reportWtf("performing boot dexopt", e);
         }
@@ -561,13 +571,9 @@
                     reportWtf("starting LockSettingsService service", e);
                 }
 
-                try {
-                    // Always start the Device Policy Manager, so that the API is compatible with
-                    // API8.
-                    mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
-                } catch (Throwable e) {
-                    reportWtf("starting DevicePolicyService", e);
-                }
+                // Always start the Device Policy Manager, so that the API is compatible with
+                // API8.
+                mSystemServiceManager.startService(DevicePolicyManagerService.Lifecycle.class);
             }
 
             if (!disableSystemUI) {
@@ -638,39 +644,17 @@
                     reportWtf("starting NetworkPolicy Service", e);
                 }
 
-                try {
-                    mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
-                } catch (Throwable e) {
-                    reportWtf("starting Wi-Fi P2pService", e);
-                }
+                mSystemServiceManager.startService(WIFI_P2P_SERVICE_CLASS);
 
-                try {
-                    mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
-                } catch (Throwable e) {
-                    reportWtf("starting Wi-Fi PasspointService", e);
-                }
+                mSystemServiceManager.startService(WIFI_PASSPOINT_SERVICE_CLASS);
 
-                try {
-                    mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
-                } catch (Throwable e) {
-                    reportWtf("starting Wi-Fi Service", e);
-                }
+                mSystemServiceManager.startService(WIFI_SERVICE_CLASS);
 
-                try {
-                    Slog.i(TAG, "Wi-Fi Scanning Service");
-                    mSystemServiceManager.startService(
+                mSystemServiceManager.startService(
                             "com.android.server.wifi.WifiScanningService");
 
-                } catch (Throwable e) {
-                    reportWtf("starting Wi-Fi Scanning Service", e);
-                }
-
                 if (!isEmulator) {
-                    try {
-                        mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
-                    } catch (Throwable e) {
-                        reportWtf("starting Ethernet Service", e);
-                    }
+                    mSystemServiceManager.startService(ETHERNET_SERVICE_CLASS);
                 } else {
                     // Don't start the Ethernet service on the emulator because
                     // it interferes with qemu's SLIRP emulation, which uses
@@ -714,7 +698,7 @@
              * AppWidget Provider. Make sure MountService is completely started
              * first before continuing.
              */
-            if (mountService != null && !onlyCore) {
+            if (mountService != null && !mOnlyCore) {
                 mountService.waitForAsecScan();
             }
 
@@ -812,14 +796,11 @@
             }
 
             if (!disableNonCoreServices) {
-                try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST) ||
-                            pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY)) {
-                        // Manage USB host and device support
-                        mSystemServiceManager.startService(USB_SERVICE_CLASS);
-                    }
-                } catch (Throwable e) {
-                    reportWtf("starting UsbService", e);
+                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
+                        || mPackageManager.hasSystemFeature(
+                                PackageManager.FEATURE_USB_ACCESSORY)) {
+                    // Manage USB host and device support
+                    mSystemServiceManager.startService(USB_SERVICE_CLASS);
                 }
 
                 try {
@@ -839,20 +820,12 @@
             mSystemServiceManager.startService(JobSchedulerService.class);
 
             if (!disableNonCoreServices) {
-                try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
-                        mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
-                    }
-                } catch (Throwable e) {
-                    Slog.e(TAG, "Failure starting Backup Service", e);
+                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_BACKUP)) {
+                    mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
                 }
 
-                try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
-                        mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
-                    }
-                } catch (Throwable e) {
-                    reportWtf("starting AppWidget Service", e);
+                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
+                    mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
                 }
 
                 try {
@@ -862,13 +835,8 @@
                     reportWtf("starting Recognition Service", e);
                 }
 
-                try {
-                    if (pm.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
-                        Slog.i(TAG, "Voice Recognition Service");
-                        mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
-                    }
-                } catch (Throwable e) {
-                    reportWtf("starting Voice Recognition Service", e);
+                if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_VOICE_RECOGNIZERS)) {
+                    mSystemServiceManager.startService(VOICE_RECOGNITION_MANAGER_SERVICE_CLASS);
                 }
             }
 
@@ -934,45 +902,17 @@
                 }
             }
 
-            try {
-                Slog.i(TAG, "IdleMaintenanceService");
-                new IdleMaintenanceService(context, battery);
-            } catch (Throwable e) {
-                reportWtf("starting IdleMaintenanceService", e);
+            if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
+                mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
             }
 
-            try {
-                if (pm.hasSystemFeature(PackageManager.FEATURE_PRINTING)) {
-                    mSystemServiceManager.startService(PRINT_MANAGER_SERVICE_CLASS);
-                }
-            } catch (Throwable e) {
-                reportWtf("starting Print Service", e);
-            }
+            mSystemServiceManager.startService(RestrictionsManagerService.class);
 
-            try {
-                mSystemServiceManager.startService(RestrictionsManagerService.class);
-            } catch (Throwable e) {
-                reportWtf("starting RestrictionsManagerService", e);
-            }
+            mSystemServiceManager.startService(MediaSessionService.class);
 
-            try {
-                mSystemServiceManager.startService(MediaSessionService.class);
-            } catch (Throwable e) {
-                reportWtf("starting MediaSessionService", e);
-            }
+            mSystemServiceManager.startService(HdmiControlService.class);
 
-            try {
-                mSystemServiceManager.startService(HdmiControlService.class);
-            } catch (Throwable e) {
-                reportWtf("starting HdmiControlService", e);
-            }
-
-            try {
-                Slog.i(TAG, "TvInputManagerService");
-                mSystemServiceManager.startService(TvInputManagerService.class);
-            } catch (Throwable e) {
-                reportWtf("starting TvInputManagerService", e);
-            }
+            mSystemServiceManager.startService(TvInputManagerService.class);
 
             if (!disableNonCoreServices) {
                 try {
@@ -983,35 +923,20 @@
                     reportWtf("starting MediaRouterService", e);
                 }
 
-                try {
-                    Slog.i(TAG, "Trust Manager");
-                    mSystemServiceManager.startService(TrustManagerService.class);
-                } catch (Throwable e) {
-                    Slog.e(TAG, "Failure starting TrustManagerService", e);
-                }
+                mSystemServiceManager.startService(TrustManagerService.class);
 
-                try {
-                    Slog.i(TAG, "Fingerprint Manager");
-                    mSystemServiceManager.startService(FingerprintService.class);
-                } catch (Throwable e) {
-                    Slog.e(TAG, "Failure starting FingerprintService", e);
-                }
+                mSystemServiceManager.startService(FingerprintService.class);
 
                 try {
                     Slog.i(TAG, "BackgroundDexOptService");
-                    new BackgroundDexOptService(context);
+                    BackgroundDexOptService.schedule(context);
                 } catch (Throwable e) {
                     reportWtf("starting BackgroundDexOptService", e);
                 }
 
             }
 
-            try {
-                Slog.i(TAG, "LauncherAppsService");
-                mSystemServiceManager.startService(LauncherAppsService.class);
-            } catch (Throwable t) {
-                reportWtf("starting LauncherAppsService", t);
-            }
+            mSystemServiceManager.startService(LauncherAppsService.class);
         }
 
         // Before things start rolling, be sure we have decided whether
@@ -1068,27 +993,26 @@
 
         try {
             // TODO: use boot phase
-            mPowerManagerService.systemReady();
+            mPowerManagerService.systemReady(mActivityManagerService.getAppOpsService());
         } catch (Throwable e) {
             reportWtf("making Power Manager Service ready", e);
         }
 
         try {
-            pm.systemReady();
+            mPackageManagerService.systemReady();
         } catch (Throwable e) {
             reportWtf("making Package Manager Service ready", e);
         }
 
         try {
             // TODO: use boot phase and communicate these flags some other way
-            mDisplayManagerService.systemReady(safeMode, onlyCore);
+            mDisplayManagerService.systemReady(safeMode, mOnlyCore);
         } catch (Throwable e) {
             reportWtf("making Display Manager Service ready", e);
         }
 
         // These are needed to propagate to the runnable below.
         final MountService mountServiceF = mountService;
-        final BatteryService batteryF = battery;
         final NetworkManagementService networkManagementF = networkManagement;
         final NetworkStatsService networkStatsF = networkStats;
         final NetworkPolicyManagerService networkPolicyF = networkPolicy;
@@ -1137,11 +1061,6 @@
                     reportWtf("making Mount Service ready", e);
                 }
                 try {
-                    if (batteryF != null) batteryF.systemReady();
-                } catch (Throwable e) {
-                    reportWtf("making Battery Service ready", e);
-                }
-                try {
                     if (networkScoreF != null) networkScoreF.systemReady();
                 } catch (Throwable e) {
                     reportWtf("making Network Score Service ready", e);
diff --git a/telecomm/java/android/telecomm/CallInfo.java b/telecomm/java/android/telecomm/CallInfo.java
index cb7f2dc..4de9373 100644
--- a/telecomm/java/android/telecomm/CallInfo.java
+++ b/telecomm/java/android/telecomm/CallInfo.java
@@ -52,8 +52,7 @@
     private final GatewayInfo mGatewayInfo;
 
     /**
-     * Additional information that can be persisted. For example, extra handoff information can
-     * attached to a call using {@link CallServiceSelectorAdapter#setHandoffInfo(String,Uri,Bundle).
+     * Additional information that can be persisted.
      */
     private final Bundle mExtras;
 
diff --git a/telecomm/java/android/telecomm/CallService.java b/telecomm/java/android/telecomm/CallService.java
index 0b5981c..cf7c901 100644
--- a/telecomm/java/android/telecomm/CallService.java
+++ b/telecomm/java/android/telecomm/CallService.java
@@ -49,21 +49,20 @@
 public abstract class CallService extends Service {
 
     private static final int MSG_SET_CALL_SERVICE_ADAPTER = 1;
-    private static final int MSG_IS_COMPATIBLE_WITH = 2;
-    private static final int MSG_CALL = 3;
-    private static final int MSG_ABORT = 4;
-    private static final int MSG_SET_INCOMING_CALL_ID = 5;
-    private static final int MSG_ANSWER = 6;
-    private static final int MSG_REJECT = 7;
-    private static final int MSG_DISCONNECT = 8;
-    private static final int MSG_HOLD = 9;
-    private static final int MSG_UNHOLD = 10;
-    private static final int MSG_ON_AUDIO_STATE_CHANGED = 11;
-    private static final int MSG_PLAY_DTMF_TONE = 12;
-    private static final int MSG_STOP_DTMF_TONE = 13;
-    private static final int MSG_CONFERENCE = 14;
-    private static final int MSG_SPLIT_FROM_CONFERENCE = 15;
-    private static final int MSG_ON_POST_DIAL_CONTINUE = 16;
+    private static final int MSG_CALL = 2;
+    private static final int MSG_ABORT = 3;
+    private static final int MSG_SET_INCOMING_CALL_ID = 4;
+    private static final int MSG_ANSWER = 5;
+    private static final int MSG_REJECT = 6;
+    private static final int MSG_DISCONNECT = 7;
+    private static final int MSG_HOLD = 8;
+    private static final int MSG_UNHOLD = 9;
+    private static final int MSG_ON_AUDIO_STATE_CHANGED = 10;
+    private static final int MSG_PLAY_DTMF_TONE = 11;
+    private static final int MSG_STOP_DTMF_TONE = 12;
+    private static final int MSG_CONFERENCE = 13;
+    private static final int MSG_SPLIT_FROM_CONFERENCE = 14;
+    private static final int MSG_ON_POST_DIAL_CONTINUE = 15;
 
     /**
      * Default Handler used to consolidate binder method calls onto a single thread.
@@ -76,9 +75,6 @@
                     mAdapter = new CallServiceAdapter((ICallServiceAdapter) msg.obj);
                     onAdapterAttached(mAdapter);
                     break;
-                case MSG_IS_COMPATIBLE_WITH:
-                    isCompatibleWith((CallInfo) msg.obj);
-                    break;
                 case MSG_CALL:
                     call((CallInfo) msg.obj);
                     break;
@@ -170,11 +166,6 @@
         }
 
         @Override
-        public void isCompatibleWith(CallInfo callInfo) {
-            mMessageHandler.obtainMessage(MSG_IS_COMPATIBLE_WITH, callInfo).sendToTarget();
-        }
-
-        @Override
         public void call(CallInfo callInfo) {
             mMessageHandler.obtainMessage(MSG_CALL, callInfo).sendToTarget();
         }
@@ -300,21 +291,10 @@
     }
 
     /**
-     * Determines if the CallService can place the specified call. Response is sent via
-     * {@link CallServiceAdapter#setIsCompatibleWith}. When responding, the correct call ID must be
-     * specified.  Only used in the context of outgoing calls and call switching (handoff).
-     *
-     * @param callInfo The details of the relevant call.
-     */
-    public abstract void isCompatibleWith(CallInfo callInfo);
-
-    /**
      * Attempts to call the relevant party using the specified call's handle, be it a phone number,
      * SIP address, or some other kind of user ID.  Note that the set of handle types is
      * dynamically extensible since call providers should be able to implement arbitrary
-     * handle-calling systems.  See {@link #isCompatibleWith}. It is expected that the
-     * call service respond via {@link CallServiceAdapter#handleSuccessfulOutgoingCall(String)}
-     * if it can successfully make the call.  Only used in the context of outgoing calls.
+     * handle-calling systems.
      *
      * @param callInfo The details of the relevant call.
      */
diff --git a/telecomm/java/android/telecomm/CallServiceAdapter.java b/telecomm/java/android/telecomm/CallServiceAdapter.java
index 0ba8161..2c5f078 100644
--- a/telecomm/java/android/telecomm/CallServiceAdapter.java
+++ b/telecomm/java/android/telecomm/CallServiceAdapter.java
@@ -37,22 +37,6 @@
     }
 
     /**
-     * Receives confirmation of a call service's ability to place a call. This method is used in
-     * response to {@link CallService#isCompatibleWith}.
-     *
-     * @param callId The identifier of the call for which compatibility is being received. This ID
-     *     should correspond to the ID given as part of the call information in
-     *     {@link CallService#isCompatibleWith}.
-     * @param isCompatible True if the call service can place the call.
-     */
-    public void setIsCompatibleWith(String callId, boolean isCompatible) {
-        try {
-            mAdapter.setIsCompatibleWith(callId, isCompatible);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
      * Provides Telecomm with the details of an incoming call. An invocation of this method must
      * follow {@link CallService#setIncomingCallId} and use the call ID specified therein. Upon
      * the invocation of this method, Telecomm will bring up the incoming-call interface where the
diff --git a/telecomm/java/android/telecomm/CallServiceSelector.java b/telecomm/java/android/telecomm/CallServiceSelector.java
deleted file mode 100644
index c9c6ff6..0000000
--- a/telecomm/java/android/telecomm/CallServiceSelector.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * Copyright (C) 2014 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.telecomm;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-
-import com.android.internal.os.SomeArgs;
-import com.android.internal.telecomm.ICallServiceSelector;
-import com.android.internal.telecomm.ICallServiceSelectorAdapter;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * Allows for the organization of {@link CallService}s for outbound calls. Given a call and list of
- * {@link CallService} IDs, order the list in terms of priority and return it using
- * {@link #select(CallInfo, List)}.
- */
-public abstract class CallServiceSelector extends Service {
-    private static final int MSG_SET_CALL_SERVICE_SELECTOR_ADAPTER = 0;
-    private static final int MSG_SELECT = 1;
-
-    private final HashMap<String, CallInfo> mCalls = new HashMap<String, CallInfo>();
-
-    /** Handler to move client-bound method calls to the main thread. */
-    private final Handler mHandler = new Handler(Looper.getMainLooper()) {
-        @Override
-        public void handleMessage(Message msg) {
-            switch (msg.what) {
-                case MSG_SET_CALL_SERVICE_SELECTOR_ADAPTER:
-                    mAdapter = new CallServiceSelectorAdapter(
-                            (ICallServiceSelectorAdapter) msg.obj);
-                    onAdapterAttached(mAdapter);
-                    break;
-                case MSG_SELECT:
-                    SomeArgs args = (SomeArgs) msg.obj;
-                    try {
-                        select((CallInfo) args.arg1, (List<CallServiceDescriptor>) args.arg2);
-                    } finally {
-                        args.recycle();
-                    }
-                    break;
-            }
-        }
-    };
-
-    /** Manages the binder calls so that the implementor does not need to deal with it. */
-    private final class CallServiceSelectorBinder extends ICallServiceSelector.Stub {
-        @Override
-        public void setCallServiceSelectorAdapter(ICallServiceSelectorAdapter adapter) {
-            mHandler.obtainMessage(MSG_SET_CALL_SERVICE_SELECTOR_ADAPTER, adapter)
-                    .sendToTarget();
-        }
-
-        @Override
-        public void select(CallInfo callInfo, List<CallServiceDescriptor> descriptors) {
-            SomeArgs args = SomeArgs.obtain();
-            args.arg1 = callInfo;
-            args.arg2 = descriptors;
-            mHandler.obtainMessage(MSG_SELECT, args).sendToTarget();
-        }
-
-        @Override
-        public void onCallUpdated(CallInfo callInfo) {
-            mCalls.put(callInfo.getId(), callInfo);
-        }
-
-        @Override
-        public void onCallRemoved(String callId) {
-            mCalls.remove(callId);
-        }
-    }
-
-    private final CallServiceSelectorBinder mBinder;
-
-    private CallServiceSelectorAdapter mAdapter = null;
-
-    protected CallServiceSelector() {
-        mBinder = new CallServiceSelectorBinder();
-    }
-
-    @Override
-    public final IBinder onBind(Intent intent) {
-        return mBinder;
-    }
-
-    /**
-     * Returns a list of all calls managed by this selector.
-     */
-    protected final Collection<CallInfo> getCalls() {
-        return Collections.unmodifiableCollection(mCalls.values());
-    }
-
-    /**
-     * @return The attached {@link CallServiceSelectorAdapter} if attached, or null otherwise.
-     */
-    protected final CallServiceSelectorAdapter getAdapter() {
-        return mAdapter;
-    }
-
-    /**
-     * Cancel the outgoing call. Any subsequent calls to {@link #select(CallInfo, List)} will be
-     * ignored.
-     *
-     * @param callInfo The call to canceled.
-     */
-    protected final void cancelOutgoingCall(CallInfo callInfo) {
-        getAdapter().cancelOutgoingCall(callInfo.getId());
-    }
-
-    /**
-     * Lifecycle callback which is called when this {@link CallServiceSelector} has been attached
-     * to a {@link CallServiceSelectorAdapter}, indicating {@link #getAdapter()} is now safe to use.
-     *
-     * @param adapter The adapter now attached to this call service selector.
-     */
-    protected void onAdapterAttached(CallServiceSelectorAdapter adapter) {
-    }
-
-    /**
-     * Given a list of {@link CallServiceDescriptor}s, order them into a prioritized list and return
-     * them through
-     * {@link CallServiceSelectorAdapter#setSelectedCallServices(String,List)}.
-     *
-     * @param callInfo The call being placed using the {@link CallService}s.
-     * @param descriptors The descriptors of the available {@link CallService}s with which to place
-     *            the call.
-     */
-    protected abstract void select(CallInfo callInfo, List<CallServiceDescriptor> descriptors);
-}
diff --git a/telecomm/java/android/telecomm/CallServiceSelectorAdapter.java b/telecomm/java/android/telecomm/CallServiceSelectorAdapter.java
deleted file mode 100644
index 4d2e8aa..0000000
--- a/telecomm/java/android/telecomm/CallServiceSelectorAdapter.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * Copyright (C) 2014 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.telecomm;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.RemoteException;
-import android.telecomm.CallServiceDescriptor;
-
-import com.android.internal.telecomm.ICallServiceSelectorAdapter;
-
-import java.util.List;
-
-/**
- * Provides methods for ICallServiceSelector implementations to interact with Telecomm.
- */
-public final class CallServiceSelectorAdapter {
-    private final ICallServiceSelectorAdapter mAdapter;
-
-    /**
-     * {@hide}
-     */
-    public CallServiceSelectorAdapter(ICallServiceSelectorAdapter adapter) {
-        mAdapter = adapter;
-    }
-
-    /**
-     * Records the sorted set of call services that are preferred by the corresponding
-     * call-service selector.
-     *
-     * @param callId The ID of the call to complete.
-     * @param selectedCallServiceDescriptors The prioritized list of preferred call-service
-     *        descriptors to use for completing the call.
-     */
-    public void setSelectedCallServices(
-            String callId,
-            List<CallServiceDescriptor> selectedCallServiceDescriptors) {
-        try {
-            mAdapter.setSelectedCallServices(callId, selectedCallServiceDescriptors);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Cancels the specified outgoing call.
-     *
-     * @param callId The ID of the call to cancel.
-     */
-    public void cancelOutgoingCall(String callId) {
-        try {
-            mAdapter.cancelOutgoingCall(callId);
-        } catch (RemoteException e) {
-        }
-    }
-
-    /**
-     * Associates handoff information with an ongoing call. Calls can switch from one call service
-     * to another. Setting handle to a non-null value marks the call as switchable.
-     *
-     * @param callId The ID of the call to set handoff information for.
-     * @param handle The handle used to place the call when switching.
-     * @param extras Optional extra that's attached to the call.
-     */
-    public void setHandoffInfo(String callId, Uri handle, Bundle extras) {
-        try {
-            mAdapter.setHandoffInfo(callId, handle, extras);
-        } catch (RemoteException e) {
-        }
-    }
-}
diff --git a/telecomm/java/android/telecomm/Connection.java b/telecomm/java/android/telecomm/Connection.java
index 164eeff..1783327 100644
--- a/telecomm/java/android/telecomm/Connection.java
+++ b/telecomm/java/android/telecomm/Connection.java
@@ -356,6 +356,13 @@
     }
 
     /**
+     * Returns whether this connection is capable of being conferenced.
+     */
+    public boolean isConferenceCapable() {
+        return mIsConferenceCapable;
+    }
+
+    /**
      * Sets the value of the {@link #getHandle()} property and notifies listeners.
      *
      * @param handle The new handle.
diff --git a/telecomm/java/android/telecomm/ConnectionService.java b/telecomm/java/android/telecomm/ConnectionService.java
index d974509..5aba941 100644
--- a/telecomm/java/android/telecomm/ConnectionService.java
+++ b/telecomm/java/android/telecomm/ConnectionService.java
@@ -20,15 +20,9 @@
 import android.os.Bundle;
 import android.telephony.DisconnectCause;
 
-import android.os.SystemClock;
-
 import java.util.Collection;
 import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * A {@link android.app.Service} that provides telephone connections to
@@ -120,28 +114,6 @@
     };
 
     @Override
-    public final void isCompatibleWith(final CallInfo callInfo) {
-        Log.d(this, "isCompatibleWith %s", callInfo);
-        onFindSubscriptions(
-                callInfo.getHandle(),
-                new Response<Uri, Subscription>() {
-                    @Override
-                    public void onResult(Uri handle, Subscription... result) {
-                        boolean isCompatible = result.length > 0;
-                        Log.d(this, "adapter setIsCompatibleWith ");
-                        getAdapter().setIsCompatibleWith(callInfo.getId(), isCompatible);
-                    }
-
-                    @Override
-                    public void onError(Uri handle, int code, String msg) {
-                        Log.w(this, "Error in onFindSubscriptions %s %d %s", handle, code, msg);
-                        getAdapter().setIsCompatibleWith(callInfo.getId(), false);
-                    }
-                }
-        );
-    }
-
-    @Override
     public final void call(final CallInfo callInfo) {
         Log.d(this, "call %s", callInfo);
         onCreateConnections(
@@ -349,16 +321,6 @@
     }
 
     /**
-     * Find a set of Subscriptions matching a given handle (e.g. phone number).
-     *
-     * @param handle A handle (e.g. phone number) with which to connect.
-     * @param callback A callback for providing the result.
-     */
-    public void onFindSubscriptions(
-            Uri handle,
-            Response<Uri, Subscription> callback) {}
-
-    /**
      * Create a Connection given a request.
      *
      * @param request Data encapsulating details of the desired Connection.
diff --git a/telecomm/java/android/telecomm/InCallService.java b/telecomm/java/android/telecomm/InCallService.java
index c7dd23a..31291fb 100644
--- a/telecomm/java/android/telecomm/InCallService.java
+++ b/telecomm/java/android/telecomm/InCallService.java
@@ -156,7 +156,7 @@
     }
 
     /**
-     * @return The attached {@link CallServiceSelectorAdapter} if attached, or null otherwise.
+     * @return The attached {@link InCallAdapter} if attached, or null otherwise.
      */
     protected final InCallAdapter getAdapter() {
         return mAdapter;
diff --git a/telecomm/java/android/telecomm/PhoneApplication.java b/telecomm/java/android/telecomm/PhoneApplication.java
deleted file mode 100644
index 1da54e0..0000000
--- a/telecomm/java/android/telecomm/PhoneApplication.java
+++ /dev/null
@@ -1,183 +0,0 @@
-package android.telecomm;
-
-import android.annotation.SystemApi;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.provider.Settings;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.internal.telecomm.ITelecommService;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Class for managing the primary phone application that will receive incoming calls, and be allowed
- * to make emergency outgoing calls.
- *
- * @hide
- */
-public class PhoneApplication {
-    private static final String TAG = PhoneApplication.class.getSimpleName();
-    private static final String TELECOMM_SERVICE_NAME = "telecomm";
-
-    /**
-     * Sets the specified package name as the default phone application. The caller of this method
-     * needs to have permission to write to secure settings.
-     *
-     * @hide
-     * */
-    @SystemApi
-    public static void setDefaultPhoneApplication(String packageName, Context context) {
-        // Get old package name
-        String oldPackageName = Settings.Secure.getString(context.getContentResolver(),
-                Settings.Secure.PHONE_DEFAULT_APPLICATION);
-
-        if (packageName != null && oldPackageName != null && packageName.equals(oldPackageName)) {
-            // No change
-            return;
-        }
-
-        // Only make the change if the new package belongs to a valid phone application
-        List<ComponentName> componentNames = getInstalledPhoneApplications(context);
-        ComponentName foundComponent = null;
-        for (ComponentName componentName : componentNames) {
-            if (TextUtils.equals(componentName.getPackageName(), packageName)) {
-                foundComponent = componentName;
-                break;
-            }
-        }
-
-        if (foundComponent != null) {
-            // Update the secure setting.
-            Settings.Secure.putString(context.getContentResolver(),
-                    Settings.Secure.PHONE_DEFAULT_APPLICATION, foundComponent.getPackageName());
-        }
-    }
-
-    /**
-     * Returns the installed phone application that will be used to receive incoming calls, and is
-     * allowed to make emergency calls.
-     *
-     * The application will be returned in order of preference:
-     * 1) User selected phone application (if still installed)
-     * 2) Pre-installed system dialer (if not disabled)
-     * 3) Null
-     *
-     * @hide
-     * */
-    @SystemApi
-    public static ComponentName getDefaultPhoneApplication(Context context) {
-        String defaultPackageName = Settings.Secure.getString(context.getContentResolver(),
-                Settings.Secure.PHONE_DEFAULT_APPLICATION);
-
-        final List<ComponentName> componentNames = getInstalledPhoneApplications(context);
-        if (!TextUtils.isEmpty(defaultPackageName)) {
-            for (ComponentName componentName : componentNames) {
-                if (TextUtils.equals(componentName.getPackageName(), defaultPackageName)) {
-                    return componentName;
-                }
-            }
-        }
-
-        // No user-set dialer found, fallback to system dialer
-        ComponentName systemDialer = null;
-        try {
-            systemDialer = getTelecommService().getSystemPhoneApplication();
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelecommService#getSystemPhoneApplication", e);
-            return null;
-        }
-
-        if (systemDialer == null) {
-            // No system dialer configured at build time
-            return null;
-        }
-
-        // Verify that the system dialer has not been disabled.
-        return getComponentName(componentNames, systemDialer.getPackageName());
-    }
-
-    /**
-     * Returns a list of installed and available phone applications.
-     *
-     * In order to appear in the list, a phone application must implement an intent-filter with
-     * the DIAL intent for the following schemes:
-     *
-     * 1) Empty scheme
-     * 2) tel Uri scheme
-     *
-     * @hide
-     **/
-    @SystemApi
-    public static List<ComponentName> getInstalledPhoneApplications(Context context) {
-        PackageManager packageManager = context.getPackageManager();
-
-        // Get the list of apps registered for the DIAL intent with empty scheme
-        Intent intent = new Intent(Intent.ACTION_DIAL);
-        List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
-
-        List<ComponentName> componentNames = new ArrayList<ComponentName> ();
-
-        for (ResolveInfo resolveInfo : resolveInfoList) {
-            final ActivityInfo activityInfo = resolveInfo.activityInfo;
-            if (activityInfo == null) {
-                continue;
-            }
-            final ComponentName componentName =
-                    new ComponentName(activityInfo.packageName, activityInfo.name);
-            componentNames.add(componentName);
-        }
-
-        // TODO: Filter for apps that don't handle DIAL intent with tel scheme
-        return componentNames;
-    }
-
-    /**
-     * Returns the {@link ComponentName} for the installed phone application for a given package
-     * name.
-     *
-     * @param context A valid context.
-     * @param packageName to retrieve the {@link ComponentName} for.
-     *
-     * @return The {@link ComponentName} for the installed phone application corresponding to the
-     * package name, or null if none is found.
-     *
-     * @hide
-     */
-    @SystemApi
-    public static ComponentName getPhoneApplicationForPackageName(Context context,
-            String packageName) {
-        return getComponentName(getInstalledPhoneApplications(context), packageName);
-    }
-
-    /**
-     * Returns the component from a list of application components that corresponds to the package
-     * name.
-     *
-     * @param componentNames A list of component names
-     * @param packageName The package name to look for
-     * @return The {@link ComponentName} that matches the provided packageName, or null if not
-     *         found.
-     */
-    private static ComponentName getComponentName(List<ComponentName> componentNames,
-            String packageName) {
-        for (ComponentName componentName : componentNames) {
-            if (TextUtils.equals(packageName, componentName.getPackageName())) {
-                return componentName;
-            }
-        }
-        return null;
-    }
-
-    private static ITelecommService getTelecommService() {
-        return ITelecommService.Stub.asInterface(ServiceManager.getService(TELECOMM_SERVICE_NAME));
-    }
-}
diff --git a/telecomm/java/android/telecomm/Subscription.aidl b/telecomm/java/android/telecomm/Subscription.aidl
new file mode 100644
index 0000000..6327fcc
--- /dev/null
+++ b/telecomm/java/android/telecomm/Subscription.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 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.
+ * 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.telecomm;
+
+/**
+ * {@hide}
+  */
+parcelable Subscription;
diff --git a/telecomm/java/android/telecomm/Subscription.java b/telecomm/java/android/telecomm/Subscription.java
index f187f4d..964db4a 100644
--- a/telecomm/java/android/telecomm/Subscription.java
+++ b/telecomm/java/android/telecomm/Subscription.java
@@ -16,25 +16,169 @@
 
 package android.telecomm;
 
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.telephony.Rlog;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+import java.util.MissingResourceException;
 
 /**
  * Represents a distinct subscription, line of service or call placement method that
- * a {@link ConnectionService} can use to place phone calls.
+ * the system can use to place phone calls.
  */
 public class Subscription implements Parcelable {
 
-    public Subscription() {}
+    private static final int NO_DENSITY = -1;
+
+    private static final String LOG_TAG = "Subscription";
+
+    private final ComponentName mComponentName;
+    private final String mId;
+    private final Uri mHandle;
+    private final int mLabelResId;
+    private final int mShortDescriptionResId;
+    private final int mIconResId;
+    private final boolean mIsEnabled;
+    private final boolean mIsSystemDefault;
+
+    public Subscription(
+            ComponentName componentName,
+            String id,
+            Uri handle,
+            int labelResId,
+            int shortDescriptionResId,
+            int iconResId,
+            boolean isEnabled,
+            boolean isSystemDefault) {
+        mComponentName = componentName;
+        mId = id;
+        mHandle = handle;
+        mLabelResId = labelResId;
+        mShortDescriptionResId = shortDescriptionResId;
+        mIconResId = iconResId;
+        mIsSystemDefault = isSystemDefault;
+        mIsEnabled = isEnabled;
+    }
+
+    /**
+     * The {@code ComponentName} of the {@link android.telecomm.ConnectionService} which is
+     * responsible for making phone calls using this {@code Subscription}.
+     *
+     * @return A suitable {@code ComponentName}.
+     */
+    public ComponentName getComponentName() {
+        return mComponentName;
+    }
+
+    /**
+     * A unique identifier for this {@code Subscription}, generated by and meaningful to the
+     * {@link android.telecomm.ConnectionService} that created it.
+     *
+     * @return A unique identifier for this {@code Subscription}.
+     */
+    public String getId() {
+        return mId;
+    }
+
+    /**
+     * The handle (e.g., a phone number) associated with this {@code Subscription}. This represents
+     * the destination from which outgoing calls using this {@code Subscription} will appear to come
+     * from, if applicable, and the destination to which incoming calls using this
+     * {@code Subscription} may be addressed.
+     *
+     * @return A handle expressed as a {@code Uri}, for example, a phone number.
+     */
+    public Uri getHandle() {
+        return mHandle;
+    }
+
+    /**
+     * A short string label describing this {@code Subscription}.
+     *
+     * @param context The invoking {@code Context}, used for retrieving resources.
+     *
+     * @return A label for this {@code Subscription}.
+     */
+    public String getLabel(Context context) {
+        return getString(context, mLabelResId);
+    }
+
+    /**
+     * A short paragraph describing this {@code Subscription}.
+     *
+     * @param context The invoking {@code Context}, used for retrieving resources.
+     *
+     * @return A description for this {@code Subscription}.
+     */
+    public String getShortDescription(Context context) {
+        return getString(context, mShortDescriptionResId);
+    }
+
+    /**
+     * An icon to represent this {@code Subscription} in a user interface.
+     *
+     * @param context The invoking {@code Context}, used for retrieving resources.
+     *
+     * @return An icon for this {@code Subscription}.
+     */
+    public Drawable getIcon(Context context) {
+        return getIcon(context, mIconResId, NO_DENSITY);
+    }
+
+    /**
+     * An icon to represent this {@code Subscription} in a user interface.
+     *
+     * @param context The invoking {@code Context}, used for retrieving resources.
+     * @param density A display density from {@link DisplayMetrics}.
+     *
+     * @return An icon for this {@code Subscription}.
+     */
+    public Drawable getIcon(Context context, int density) {
+        return getIcon(context, mIconResId, density);
+    }
+
+    /**
+     * Whether this {@code Subscription} is enabled for use.
+     *
+     * @return {@code true} if this {@code Subscription} is enabled.
+     */
+    public boolean isEnabled() {
+        return mIsEnabled;
+    }
+
+    /**
+     * Whether this {@code Subscription} is the system default.
+     *
+     * @return {@code true} if this {@code Subscription} is the system default.
+     */
+    public boolean isSystemDefault() {
+        return mIsSystemDefault;
+    }
 
     public int describeContents() {
         return 0;
     }
 
-    public void writeToParcel(Parcel out, int flags) {}
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(mComponentName, flags);
+        out.writeString(mId);
+        out.writeString(mHandle != null ? mHandle.toString() : "");
+        out.writeInt(mLabelResId);
+        out.writeInt(mShortDescriptionResId);
+        out.writeInt(mIconResId);
+        out.writeInt(mIsEnabled ? 1 : 0);
+        out.writeInt(mIsSystemDefault ? 1 : 0);
+    }
 
-    public static final Parcelable.Creator<Subscription> CREATOR
-            = new Parcelable.Creator<Subscription>() {
+    public static final Creator<Subscription> CREATOR
+            = new Creator<Subscription>() {
         public Subscription createFromParcel(Parcel in) {
             return new Subscription(in);
         }
@@ -44,5 +188,54 @@
         }
     };
 
-    private Subscription(Parcel in) {}
+    private Subscription(Parcel in) {
+        mComponentName = in.readParcelable(getClass().getClassLoader());
+        mId = in.readString();
+        String uriString = in.readString();
+        mHandle = uriString.length() > 0 ? Uri.parse(uriString) : null;
+        mLabelResId = in.readInt();
+        mShortDescriptionResId = in.readInt();
+        mIconResId = in.readInt();
+        mIsEnabled = in.readInt() == 1;
+        mIsSystemDefault = in.readInt() == 1;
+    }
+
+    private String getString(Context context, int resId) {
+        Context packageContext;
+        try {
+            packageContext = context.createPackageContext(mComponentName.getPackageName(), 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            if (Rlog.isLoggable(LOG_TAG, Log.WARN)) {
+                Rlog.w(LOG_TAG, "Cannot find package " + mComponentName.getPackageName());
+            }
+            return null;
+        }
+        String result = packageContext.getString(resId);
+        if (result == null && Rlog.isLoggable(LOG_TAG, Log.WARN)) {
+            Rlog.w(LOG_TAG, "Cannot find string " + resId + " in package " +
+                    mComponentName.getPackageName());
+        }
+        return result;
+    }
+
+    private Drawable getIcon(Context context, int resId, int density) {
+        Context packageContext;
+        try {
+            packageContext = context.createPackageContext(mComponentName.getPackageName(), 0);
+        } catch (PackageManager.NameNotFoundException e) {
+            if (Rlog.isLoggable(LOG_TAG, Log.WARN)) {
+                Rlog.w(LOG_TAG, "Cannot find package " + mComponentName.getPackageName());
+            }
+            return null;
+        }
+        try {
+            return density == NO_DENSITY ?
+                    packageContext.getResources().getDrawable(resId) :
+                    packageContext.getResources().getDrawableForDensity(resId, density);
+        } catch (MissingResourceException e) {
+            Rlog.e(LOG_TAG, "Cannot find icon " + resId + " in package " +
+                    mComponentName.getPackageName() + ": " + e.toString());
+            return null;
+        }
+    }
 }
diff --git a/telecomm/java/android/telecomm/TelecommConstants.java b/telecomm/java/android/telecomm/TelecommConstants.java
index 0952097..4c08da0 100644
--- a/telecomm/java/android/telecomm/TelecommConstants.java
+++ b/telecomm/java/android/telecomm/TelecommConstants.java
@@ -51,26 +51,6 @@
     public static final String ACTION_CALL_SERVICE = CallService.class.getName();
 
     /**
-     * The service action used to bind to {@link CallServiceSelector} implementations.
-     */
-    public static final String ACTION_CALL_SERVICE_SELECTOR = CallServiceSelector.class.getName();
-
-    /**
-     * Activity action: Ask the user to change the default phone application. This will show a
-     * dialog that asks the user whether they want to replace the current default phone application
-     * with the one defined in {@link #EXTRA_PACKAGE_NAME}.
-     */
-    public static final String ACTION_CHANGE_DEFAULT_PHONE =
-            "android.telecomm.ACTION_CHANGE_DEFAULT_PHONE";
-
-    /**
-     * The PackageName string passed in as an extra for {@link #ACTION_CHANGE_DEFAULT_PHONE}.
-     *
-     * @see #ACTION_CHANGE_DEFAULT_PHONE
-     */
-    public static final String EXTRA_PACKAGE_NAME = "package";
-
-    /**
      * Optional extra for {@link Intent#ACTION_CALL} containing a boolean that determines whether
      * the speakerphone should be automatically turned on for an outgoing call.
      */
diff --git a/telecomm/java/com/android/internal/telecomm/ICallService.aidl b/telecomm/java/com/android/internal/telecomm/ICallService.aidl
index 827f331..62ebd54 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallService.aidl
@@ -32,8 +32,6 @@
 oneway interface ICallService {
     void setCallServiceAdapter(in ICallServiceAdapter callServiceAdapter);
 
-    void isCompatibleWith(in CallInfo callInfo);
-
     void call(in CallInfo callInfo);
 
     void abort(String callId);
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
index 6e176eb..270c551 100644
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ICallServiceAdapter.aidl
@@ -27,8 +27,6 @@
  * {@hide}
  */
 oneway interface ICallServiceAdapter {
-    void setIsCompatibleWith(String callId, boolean isCompatible);
-
     void notifyIncomingCall(in CallInfo callInfo);
 
     void handleSuccessfulOutgoingCall(String callId);
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceSelector.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceSelector.aidl
deleted file mode 100644
index 9597dc1..0000000
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceSelector.aidl
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2014 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.internal.telecomm;
-
-import android.telecomm.CallInfo;
-import android.telecomm.CallServiceDescriptor;
-
-import com.android.internal.telecomm.ICallService;
-import com.android.internal.telecomm.ICallServiceSelectorAdapter;
-
-import java.util.List;
-
-/**
- * Internal remote interface for call service selectors.
- *
- * @see android.telecomm.CallServiceSelector
- *
- * @hide
- */
-oneway interface ICallServiceSelector {
-    void setCallServiceSelectorAdapter(in ICallServiceSelectorAdapter adapter);
-
-    void select(in CallInfo callInfo, in List<CallServiceDescriptor> callServiceDescriptors);
-
-    void onCallUpdated(in CallInfo callInfo);
-
-    void onCallRemoved(String callId);
-}
diff --git a/telecomm/java/com/android/internal/telecomm/ICallServiceSelectorAdapter.aidl b/telecomm/java/com/android/internal/telecomm/ICallServiceSelectorAdapter.aidl
deleted file mode 100644
index ad71e3c..0000000
--- a/telecomm/java/com/android/internal/telecomm/ICallServiceSelectorAdapter.aidl
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2014 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.internal.telecomm;
-
-import android.net.Uri;
-import android.os.Bundle;
-import android.telecomm.CallInfo;
-import android.telecomm.CallServiceDescriptor;
-
-import java.util.List;
-
-/**
- * Internal remote interface for call service selector adapter.
- *
- * @see android.telecomm.CallServiceSelectorAdapter
- *
- * @hide
- */
-oneway interface ICallServiceSelectorAdapter {
-    void setSelectedCallServices(
-            String callId,
-            in List<CallServiceDescriptor> selectedCallServiceDescriptors);
-
-    void cancelOutgoingCall(String callId);
-
-    void setHandoffInfo(String callId, in Uri handle, in Bundle extras);
-}
diff --git a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
index 638b86a..c758c6d 100644
--- a/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
+++ b/telecomm/java/com/android/internal/telecomm/ITelecommService.aidl
@@ -16,7 +16,7 @@
 
 package com.android.internal.telecomm;
 
-import android.content.ComponentName;
+import android.telecomm.Subscription;
 
 /**
  * Interface used to interact with Telecomm. Mostly this is used by TelephonyManager for passing
@@ -42,7 +42,17 @@
     void showCallScreen(boolean showDialpad);
 
     /**
-     * Returns the component name of the phone application installed on the system partition.
+     * Gets a list of Subscriptions.
      */
-    ComponentName getSystemPhoneApplication();
+    List<Subscription> getSubscriptions();
+
+    /**
+     * Sets the enabled state of a given Subscription.
+     */
+    void setEnabled(in Subscription subscription, boolean enabled);
+
+    /**
+     * Sets a given Subscription as the system default.
+     */
+    void setSystemDefault(in Subscription subscription);
 }
diff --git a/telephony/java/android/telephony/SubInfoRecord.java b/telephony/java/android/telephony/SubInfoRecord.java
index 670def7..ced8e2f 100644
--- a/telephony/java/android/telephony/SubInfoRecord.java
+++ b/telephony/java/android/telephony/SubInfoRecord.java
@@ -105,4 +105,11 @@
         return 0;
     }
 
+    public String toString() {
+        return "{mSubId=" + mSubId + ", mIccId=" + mIccId + " mSlotId=" + mSlotId
+                + " mDisplayName=" + mDisplayName + " mNameSource=" + mNameSource
+                + " mColor=" + mColor + " mNumber=" + mNumber
+                + " mDispalyNumberFormat=" + mDispalyNumberFormat + " mDataRoaming=" + mDataRoaming
+                + " mSimIconRes=" + mSimIconRes + "}";
+    }
 }
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 859a890..79e9fd5 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -697,12 +697,16 @@
     public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId) {
         long [] subId = SubscriptionManager.getSubId(phoneId);
         if ((subId != null) && (subId.length >= 1)) {
-            if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
-            intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); //FIXME: RENAME TO PHONE_ID_KEY ??
-            intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId[0]);
+            putPhoneIdAndSubIdExtra(intent, phoneId, subId[0]);
         } else {
             logd("putPhoneIdAndSubIdExtra: no valid subs");
         }
     }
+
+    public static void putPhoneIdAndSubIdExtra(Intent intent, int phoneId, long subId) {
+        if (VDBG) logd("putPhoneIdAndSubIdExtra: phoneId=" + phoneId + " subId=" + subId);
+        intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); //FIXME: RENAME TO PHONE_ID_KEY ??
+        intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId);
+    }
 }
 
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 50bbb1e..124a8ec 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -19,15 +19,12 @@
 import android.annotation.SystemApi;
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
-import android.content.ComponentName;
 import android.content.Context;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.telephony.Rlog;
+import android.telecomm.Subscription;
 import android.util.Log;
 
 import com.android.internal.telecomm.ITelecommService;
@@ -40,7 +37,6 @@
 
 import java.io.FileInputStream;
 import java.io.IOException;
-import java.util.HashMap;
 import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -302,6 +298,17 @@
     public static final String EXTRA_INCOMING_NUMBER = "incoming_number";
 
     /**
+     * The lookup key used with an {@link android.content.Intent#ACTION_CALL} or
+     * {@link android.content.Intent#ACTION_DIAL} {@code Intent} for a {@link Subscription}
+     * object indicating a preference when making a phone connection.
+     *
+     * <p class="note">
+     * Retrieve with
+     * {@link android.content.Intent#getParcelableExtra(String)}.
+     */
+    public static final String EXTRA_SUBSCRIPTION = "subscription";
+
+    /**
      * Broadcast intent action indicating that a precise call state
      * (cellular) on the device has changed.
      *
@@ -3028,28 +3035,6 @@
 
     /** @hide */
     @SystemApi
-    public int enableApnType(String type) {
-        try {
-            return getITelephony().enableApnType(type);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#enableApnType", e);
-        }
-        return PhoneConstants.APN_REQUEST_FAILED;
-    }
-
-    /** @hide */
-    @SystemApi
-    public int disableApnType(String type) {
-        try {
-            return getITelephony().disableApnType(type);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error calling ITelephony#disableApnType", e);
-        }
-        return PhoneConstants.APN_REQUEST_FAILED;
-    }
-
-    /** @hide */
-    @SystemApi
     public boolean enableDataConnectivity() {
         try {
             return getITelephony().enableDataConnectivity();
@@ -3112,4 +3097,40 @@
         }
         return false;
     }
+
+    /**
+     * Return a list of Subscriptions that can be used to indicate a preference when making
+     * a phone call.
+     *
+     * @see #EXTRA_SUBSCRIPTION
+     * @return A list of {@code Subscription} objects.
+     */
+    public List<Subscription> getSubscriptions() {
+        try {
+            return getTelecommService().getSubscriptions();
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#getSubscriptions", e);
+        }
+        return null;
+    }
+
+    /** @hide */
+    @SystemApi
+    public void setEnabled(Subscription subscription, boolean enabled) {
+        try {
+            getTelecommService().setEnabled(subscription, enabled);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setEnabled", e);
+        }
+    }
+
+    /** @hide */
+    @SystemApi
+    public void setSystemDefault(Subscription subscription) {
+        try {
+            getTelecommService().setSystemDefault(subscription);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Error calling ITelephony#setSystemDefault", e);
+        }
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/IMms.aidl b/telephony/java/com/android/internal/telephony/IMms.aidl
new file mode 100644
index 0000000..a745420
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IMms.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 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.internal.telephony;
+
+import android.app.PendingIntent;
+
+/**
+ * Service interface to handle MMS API requests
+ */
+interface IMms {
+    /**
+     * Send an MMS message
+     *
+     * @param callingPkg the package name of the calling app
+     * @param pdu the MMS message encoded in standard MMS PDU format
+     * @param locationUrl the optional location url for where this message should be sent to
+     * @param sentIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is successfully sent, or failed
+     */
+    void sendMessage(String callingPkg, in byte[] pdu, String locationUrl,
+            in PendingIntent sentIntent);
+
+    /**
+     * Download an MMS message using known location and transaction id
+     *
+     * @param callingPkg the package name of the calling app
+     * @param locationUrl the location URL of the MMS message to be downloaded, usually obtained
+     *  from the MMS WAP push notification
+     * @param downloadedIntent if not NULL this <code>PendingIntent</code> is
+     *  broadcast when the message is downloaded, or the download is failed
+     */
+    void downloadMessage(String callingPkg, String locationUrl, in PendingIntent downloadedIntent);
+}
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 9e975e9..53429b6 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -223,6 +223,17 @@
     void injectSmsPdu(in byte[] pdu, String format, in PendingIntent receivedIntent);
 
     /**
+     * Update the status of a pending (send-by-IP) SMS message and resend by PSTN if necessary.
+     * This outbound message was handled by the carrier app. If the carrier app fails to send
+     * this message, it would be resent by PSTN.
+     *
+     * @param messageRef the reference number of the SMS message.
+     * @param success True if and only if the message was sent successfully. If its value is
+     *  false, this message should be resent via PSTN.
+     */
+    void updateSmsSendStatus(int messageRef, boolean success);
+
+    /**
      * Send a multi-part text based SMS.
      *
      * @param destinationAddress the address to send the message to
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 407da87..beee616 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -310,27 +310,6 @@
      */
     void disableLocationUpdatesUsingSubId(long subId);
 
-
-    /**
-     * Enable a specific APN type.
-     */
-    int enableApnType(String type);
-
-    /**
-     * Disable a specific APN type.
-     */
-    int disableApnType(String type);
-
-    /**
-     * Enable a specific APN type with subscription.
-     */
-    int enableApnTypeUsingSub(long subId, String type);
-
-    /**
-     * Disable a specific APN type with subscription.
-     */
-    int disableApnTypeUsingSub(long subId, String type);
-
     /**
      * Allow mobile data connections.
      */
diff --git a/test-runner/Android.mk b/test-runner/Android.mk
index 0d9e4f1..b12795c 100644
--- a/test-runner/Android.mk
+++ b/test-runner/Android.mk
@@ -20,7 +20,7 @@
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-LOCAL_JAVA_LIBRARIES := core core-junit framework
+LOCAL_JAVA_LIBRARIES := core-libart core-junit framework
 LOCAL_STATIC_JAVA_LIBRARIES := junit-runner
 
 LOCAL_MODULE:= android.test.runner
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 17db1b4..1d10729 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -44,6 +44,7 @@
 import android.content.pm.VerifierDeviceIdentity;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
+import android.graphics.Bitmap;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.UserHandle;
@@ -707,17 +708,8 @@
      * @hide
      */
     @Override
-    public void addCrossProfileIntentFilter(IntentFilter filter, boolean removable,
-            int sourceUserId, int targetUserId) {
-        throw new UnsupportedOperationException();
-    }
-
-    /**
-     * @hide
-     */
-    @Override
-    public void addForwardingIntentFilter(IntentFilter filter, boolean removable, int sourceUserId,
-            int targetUserId) {
+    public void addCrossProfileIntentFilter(IntentFilter filter, int sourceUserId, int targetUserId,
+            int flags) {
         throw new UnsupportedOperationException();
     }
 
@@ -729,16 +721,24 @@
         throw new UnsupportedOperationException();
     }
 
+    /** {@hide} */
+    public PackageInstaller getPackageInstaller() {
+        throw new UnsupportedOperationException();
+    }
+
     /**
      * @hide
      */
     @Override
-    public void clearForwardingIntentFilters(int sourceUserId) {
+    public void addCrossProfileIntentsForPackage(String packageName, int sourceUserId,
+            int targetUserId) {
         throw new UnsupportedOperationException();
     }
 
-    /** {@hide} */
-    public PackageInstaller getPackageInstaller() {
+    /**
+     * @hide
+     */
+    public Bitmap getUserIcon(int userId) {
         throw new UnsupportedOperationException();
     }
 }
diff --git a/tests/IdleServiceTest/Android.mk b/tests/IdleServiceTest/Android.mk
deleted file mode 100644
index a7879c5..0000000
--- a/tests/IdleServiceTest/Android.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files)
-
-LOCAL_PACKAGE_NAME := IdleServiceTest
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PROGUARD_ENABLED := disabled
-
-include $(BUILD_PACKAGE)
diff --git a/tests/IdleServiceTest/AndroidManifest.xml b/tests/IdleServiceTest/AndroidManifest.xml
deleted file mode 100644
index 16d2324..0000000
--- a/tests/IdleServiceTest/AndroidManifest.xml
+++ /dev/null
@@ -1,59 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 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.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-        package="com.android.idleservicetest">
-
-    <application>
-        <service android:name="TestService"
-                 android:exported="true"
-                 android:enabled="true"
-                 android:permission="android.permission.BIND_IDLE_SERVICE" >
-            <intent-filter>
-                <action android:name="android.service.idle.IdleService" />
-            </intent-filter>
-        </service>
-
-        <service android:name="CrashingTestService"
-                 android:exported="true"
-                 android:enabled="true"
-                 android:permission="android.permission.BIND_IDLE_SERVICE" >
-            <intent-filter>
-                <action android:name="android.service.idle.IdleService" />
-            </intent-filter>
-        </service>
-
-        <service android:name="TimeoutTestService"
-                 android:exported="true"
-                 android:enabled="true"
-                 android:permission="android.permission.BIND_IDLE_SERVICE" >
-            <intent-filter>
-                <action android:name="android.service.idle.IdleService" />
-            </intent-filter>
-        </service>
-
-        <!-- UnpermissionedTestService should never run because it does
-             not require the necessary permission in its <service> block -->
-        <service android:name="UnpermissionedTestService"
-                 android:exported="true"
-                 android:enabled="true" >
-            <intent-filter>
-                <action android:name="android.service.idle.IdleService" />
-            </intent-filter>
-        </service>
-
-    </application>
-</manifest>
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java
deleted file mode 100644
index 022ebcf..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/CrashingTestService.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2014 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.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.os.Handler;
-import android.util.Log;
-
-public class CrashingTestService extends IdleService {
-    static final String TAG = "CrashingTestService";
-
-    String mNull = null;
-
-    @Override
-    public boolean onIdleStart() {
-        Log.i(TAG, "Idle maintenance: onIdleStart()");
-
-        Handler h = new Handler();
-        Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                Log.i(TAG, "Explicitly crashing");
-                if (mNull.equals("")) {
-                    Log.i(TAG, "won't happen");
-                }
-            }
-        };
-        Log.i(TAG, "Posting explicit crash in 15 seconds");
-        h.postDelayed(r, 15 * 1000);
-        return true;
-    }
-
-    @Override
-    public void onIdleStop() {
-        Log.i(TAG, "Idle maintenance: onIdleStop()");
-    }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java
deleted file mode 100644
index 7e9805f..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/TestService.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2014 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.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.os.Handler;
-import android.util.Log;
-
-public class TestService extends IdleService {
-    static final String TAG = "TestService";
-
-    @Override
-    public boolean onIdleStart() {
-        Log.i(TAG, "Idle maintenance: onIdleStart()");
-
-        Handler h = new Handler();
-        Runnable r = new Runnable() {
-            @Override
-            public void run() {
-                Log.i(TAG, "Explicitly finishing idle");
-                finishIdle();
-            }
-        };
-        Log.i(TAG, "Posting explicit finish in 15 seconds");
-        h.postDelayed(r, 15 * 1000);
-        return true;
-    }
-
-    @Override
-    public void onIdleStop() {
-        Log.i(TAG, "Idle maintenance: onIdleStop()");
-    }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java
deleted file mode 100644
index b2ba21b..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/TimeoutTestService.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * Copyright (C) 2014 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.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.util.Log;
-
-public class TimeoutTestService extends IdleService {
-    private static final String TAG = "TimeoutTestService";
-
-    @Override
-    public boolean onIdleStart() {
-        Log.i(TAG, "onIdleStart() but anticipating time-slice timeout");
-        return true;
-    }
-
-    @Override
-    public void onIdleStop() {
-        Log.i(TAG, "onIdleStop() so we're done");
-    }
-
-}
diff --git a/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java b/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java
deleted file mode 100644
index b9fe32b..0000000
--- a/tests/IdleServiceTest/src/com/android/idleservicetest/UnpermissionedTestService.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2014 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.idleservicetest;
-
-import android.app.maintenance.IdleService;
-import android.util.Log;
-
-// Should never be invoked because its manifest declaration does not
-// require the necessary permission.
-public class UnpermissionedTestService extends IdleService {
-    private static final String TAG = "UnpermissionedTestService";
-
-    @Override
-    public boolean onIdleStart() {
-        Log.e(TAG, "onIdleStart() for this service should never be called!");
-        return false;
-    }
-
-    @Override
-    public void onIdleStop() {
-        Log.e(TAG, "onIdleStop() for this service should never be called!");
-    }
-
-}
diff --git a/tests/JobSchedulerTestApp/res/layout/activity_main.xml b/tests/JobSchedulerTestApp/res/layout/activity_main.xml
index 7f4961b..d3429ff 100644
--- a/tests/JobSchedulerTestApp/res/layout/activity_main.xml
+++ b/tests/JobSchedulerTestApp/res/layout/activity_main.xml
@@ -1,12 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
     android:orientation="vertical" >
 
     <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
+        android:layout_height="match_parent"
         android:layout_weight="1"
         android:orientation="vertical">
 
@@ -54,6 +54,7 @@
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:text="@string/constraints"
+            android:layout_margin="15dp"
             android:textSize="18dp"/>
         <LinearLayout
             android:layout_width="match_parent"
@@ -83,43 +84,81 @@
                 </RadioGroup>
 
                 </LinearLayout>
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content">
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:text="@string/timing"/>
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_marginLeft="15dp"
-                    android:textSize="17dp"
-                    android:text="@string/delay"/>
-                <EditText
-                    android:id="@+id/delay_time"
-                    android:layout_width="60dp"
-                    android:layout_height="wrap_content"
-                    android:inputType="number"/>
-                <TextView
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:text="@string/deadline"
-                    android:textSize="17dp"/>
-                <EditText
-                    android:id="@+id/deadline_time"
-                    android:layout_width="60dp"
-                    android:layout_height="wrap_content"
-                    android:inputType="number"/>
-            </LinearLayout>
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content">
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/timing"/>
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:layout_marginLeft="15dp"
+                        android:textSize="17dp"
+                        android:text="@string/delay"/>
+                    <EditText
+                        android:id="@+id/delay_time"
+                        android:layout_width="60dp"
+                        android:layout_height="wrap_content"
+                        android:inputType="number"/>
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/deadline"
+                        android:textSize="17dp"/>
+                    <EditText
+                        android:id="@+id/deadline_time"
+                        android:layout_width="60dp"
+                        android:layout_height="wrap_content"
+                        android:inputType="number"/>
+                </LinearLayout>
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content">
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/charging_caption"
+                        android:layout_marginRight="15dp"/>
+                    <CheckBox
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:id="@+id/checkbox_charging"
+                        android:text="@string/charging_text"/>
+                </LinearLayout>
+                <LinearLayout
+                    android:layout_width="match_parent"
+                    android:layout_height="wrap_content">
+                    <TextView
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:text="@string/idle_caption"
+                        android:layout_marginRight="15dp"/>
+                    <CheckBox
+                        android:layout_width="wrap_content"
+                        android:layout_height="wrap_content"
+                        android:id="@+id/checkbox_idle"
+                        android:text="@string/idle_mode_text"/>
+                </LinearLayout>
 
             </LinearLayout>
         <Button
             android:id="@+id/schedule_button"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_margin="40dp"
+            android:layout_marginTop="20dp"
+            android:layout_marginLeft="40dp"
+            android:layout_marginRight="40dp"
             android:onClick="scheduleJob"
             android:text="@string/schedule_job_button_text"/>
+        <Button
+            android:id="@+id/cancel_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="40dp"
+            android:layout_marginRight="40dp"
+            android:onClick="cancelAllJobs"
+            android:text="@string/cancel_all_jobs_button_text"/>
     </LinearLayout>
-</LinearLayout>
+</ScrollView>
diff --git a/tests/JobSchedulerTestApp/res/values/strings.xml b/tests/JobSchedulerTestApp/res/values/strings.xml
index 824d4b1..eebfb19 100644
--- a/tests/JobSchedulerTestApp/res/values/strings.xml
+++ b/tests/JobSchedulerTestApp/res/values/strings.xml
@@ -20,9 +20,13 @@
     <string name="onstarttask">onStartTask</string>
     <string name="defaultparamtext">task params will show up here.</string>
     <string name="schedule_job_button_text">Schedule Job</string>
+    <string name="cancel_all_jobs_button_text">Cancel all</string>
     <string name="app_name">Job Scheduler Test</string>
     <string name="finish_job_button_text">taskFinished</string>
-    <string name="manual_sync_text">Manual Sync</string>
+    <string name="idle_mode_text">Requires device in idle mode.</string>
+    <string name="charging_caption">Charging:</string>
+    <string name="charging_text">Requires device plugged in.</string>
+    <string name="idle_caption">Idle:</string>
     <string name="constraints">Constraints</string>
     <string name="connectivity">Connectivity:</string>
     <string name="any">Any</string>
diff --git a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java
index 15050ef..e15929d 100644
--- a/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java
+++ b/tests/JobSchedulerTestApp/src/com/android/demo/jobSchedulerApp/MainActivity.java
@@ -19,7 +19,9 @@
 import android.app.Activity;
 import android.app.job.JobInfo;
 import android.app.job.JobParameters;
+import android.app.job.JobScheduler;
 import android.content.ComponentName;
+import android.content.Context;
 import android.content.Intent;
 import android.content.res.Resources;
 import android.os.Bundle;
@@ -28,6 +30,7 @@
 import android.os.Messenger;
 import android.text.TextUtils;
 import android.view.View;
+import android.widget.CheckBox;
 import android.widget.EditText;
 import android.widget.RadioButton;
 import android.widget.TextView;
@@ -60,7 +63,8 @@
         mDeadlineEditText = (EditText) findViewById(R.id.deadline_time);
         mWiFiConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_unmetered);
         mAnyConnectivityRadioButton = (RadioButton) findViewById(R.id.checkbox_any);
-
+        mRequiresChargingCheckBox = (CheckBox) findViewById(R.id.checkbox_charging);
+        mRequiresIdleCheckbox = (CheckBox) findViewById(R.id.checkbox_idle);
         mServiceComponent = new ComponentName(this, TestJobService.class);
         // Start service and provide it a way to communicate with us.
         Intent startServiceIntent = new Intent(this, TestJobService.class);
@@ -79,6 +83,9 @@
     EditText mDeadlineEditText;
     RadioButton mWiFiConnectivityRadioButton;
     RadioButton mAnyConnectivityRadioButton;
+    CheckBox mRequiresChargingCheckBox;
+    CheckBox mRequiresIdleCheckbox;
+
     ComponentName mServiceComponent;
     /** Service object to interact scheduled jobs. */
     TestJobService mTestService;
@@ -124,24 +131,32 @@
 
         String delay = mDelayEditText.getText().toString();
         if (delay != null && !TextUtils.isEmpty(delay)) {
-            builder.setMinimumLatency(Long.valueOf(delay));
+            builder.setMinimumLatency(Long.valueOf(delay) * 1000);
         }
         String deadline = mDeadlineEditText.getText().toString();
         if (deadline != null && !TextUtils.isEmpty(deadline)) {
-            builder.setOverrideDeadline(Long.valueOf(deadline));
+            builder.setOverrideDeadline(Long.valueOf(deadline) * 1000);
         }
-        boolean requiresUnmetered = mWiFiConnectivityRadioButton.isSelected();
-        boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isSelected();
+        boolean requiresUnmetered = mWiFiConnectivityRadioButton.isChecked();
+        boolean requiresAnyConnectivity = mAnyConnectivityRadioButton.isChecked();
         if (requiresUnmetered) {
             builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.UNMETERED);
         } else if (requiresAnyConnectivity) {
             builder.setRequiredNetworkCapabilities(JobInfo.NetworkType.ANY);
         }
+        builder.setRequiresDeviceIdle(mRequiresIdleCheckbox.isChecked());
+        builder.setRequiresCharging(mRequiresChargingCheckBox.isChecked());
 
         mTestService.scheduleJob(builder.build());
 
     }
 
+    public void cancelAllJobs(View v) {
+        JobScheduler tm =
+                (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
+        tm.cancelAll();
+    }
+
     /**
      * UI onclick listener to call jobFinished() in our service.
      */
diff --git a/tests/VectorDrawableTest/AndroidManifest.xml b/tests/VectorDrawableTest/AndroidManifest.xml
index db2efc3..890214f 100644
--- a/tests/VectorDrawableTest/AndroidManifest.xml
+++ b/tests/VectorDrawableTest/AndroidManifest.xml
@@ -33,6 +33,16 @@
             </intent-filter>
         </activity>
         <activity
+            android:name="BitmapDrawableDupe"
+            android:label="Bitmap Performance of clones" >
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+
+                <category android:name="com.android.test.dynamic.TEST" />
+            </intent-filter>
+
+        </activity>
+        <activity
             android:name="VectorDrawableAnimation"
             android:label="VectorTestAnimation" >
             <intent-filter>
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml
index 72beba2..0c1073e 100644
--- a/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml
+++ b/tests/VectorDrawableTest/res/anim/trim_path_animation03.xml
@@ -21,6 +21,8 @@
         android:duration="6000"
         android:propertyName="rotation"
         android:valueFrom="0"
-        android:valueTo="360"/>
+        android:valueTo="360"
+        android:interpolator="@interpolator/custom_path_interpolator"
+        />
 
 </set>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml
index ff86668..4d0aae1 100644
--- a/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml
+++ b/tests/VectorDrawableTest/res/anim/trim_path_animation04.xml
@@ -16,11 +16,9 @@
 -->
 
 <set xmlns:android="http://schemas.android.com/apk/res/android" >
-
     <objectAnimator
         android:duration="9000"
-        android:propertyName="rotation"
-        android:valueFrom="0"
-        android:valueTo="360"/>
-
+        android:propertyXName="translateX"
+        android:propertyYName="translateY"
+        android:pathData="m0,0 q 150, 300 150, 0 t 150, 0, t 150, 0 t -150 0 t -150 0 t -150 0 z" />
 </set>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/anim/trim_path_animation05.xml b/tests/VectorDrawableTest/res/anim/trim_path_animation05.xml
new file mode 100644
index 0000000..7012f4b
--- /dev/null
+++ b/tests/VectorDrawableTest/res/anim/trim_path_animation05.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+     android:ordering="sequentially" >
+
+    <objectAnimator
+        android:duration="3000"
+        android:propertyName="pathData"
+        android:valueFrom="@string/triangle"
+        android:valueTo="@string/rectangle"
+        android:valueType="pathType"/>
+
+    <objectAnimator
+        android:duration="3000"
+        android:propertyName="pathData"
+        android:valueFrom="@string/rectangle2"
+        android:valueTo="@string/equal2"
+        android:valueType="pathType"/>
+
+</set>
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg b/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg
new file mode 100644
index 0000000..dc8c197
--- /dev/null
+++ b/tests/VectorDrawableTest/res/drawable-nodpi/bitmap_drawable01.jpg
Binary files differ
diff --git a/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml b/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
index b8681b6..b37b19f 100644
--- a/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
+++ b/tests/VectorDrawableTest/res/drawable/animation_vector_drawable01.xml
@@ -19,9 +19,13 @@
     <target
         android:name="pie1"
         android:animation="@anim/trim_path_animation01" />
+
     <target
         android:name="v"
         android:animation="@anim/trim_path_animation02" />
+    <target
+        android:name="v"
+        android:animation="@anim/trim_path_animation05" />
 
     <target
         android:name="rotationGroup"
@@ -32,5 +36,8 @@
     <target
         android:name="rotationGroupBlue"
         android:animation="@anim/trim_path_animation03" />
+    <target
+        android:name="rotationGroup"
+        android:animation="@anim/trim_path_animation04" />
 
-</animated-vector>
\ No newline at end of file
+</animated-vector>
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
index e28ec41..a212def 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable12.xml
@@ -32,27 +32,21 @@
             android:name="pie1"
             android:fill="#00000000"
             android:pathData="M300,70 a230,230 0 1,0 1,0 z"
-            android:stroke="#FF00FF00"
+            android:stroke="#FF777777"
             android:strokeWidth="70"
             android:trimPathEnd=".75"
             android:trimPathOffset="0"
             android:trimPathStart="0" />
         <path
             android:name="v"
-            android:fill="#FF00FF00"
-            android:pathData="M300,70 l 0,-70 70,70 -70,70z" />
+            android:fill="#000000"
+            android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
 
         <group
             android:name="translateToCenterGroup"
             android:rotation="0.0"
             android:translateX="200.0"
             android:translateY="200.0" >
-            <path
-                android:name="twoLines"
-                android:pathData="@string/twoLinePathData"
-                android:stroke="#FFFF0000"
-                android:strokeWidth="20" />
-
             <group
                 android:name="rotationGroup2"
                 android:pivotX="0.0"
@@ -61,7 +55,7 @@
                 <path
                     android:name="twoLines1"
                     android:pathData="@string/twoLinePathData"
-                    android:stroke="#FF00FF00"
+                    android:stroke="#FFFF0000"
                     android:strokeWidth="20" />
 
                 <group
diff --git a/tests/VectorDrawableTest/res/interpolator/custom_path_interpolator.xml b/tests/VectorDrawableTest/res/interpolator/custom_path_interpolator.xml
new file mode 100644
index 0000000..0cffa0a
--- /dev/null
+++ b/tests/VectorDrawableTest/res/interpolator/custom_path_interpolator.xml
@@ -0,0 +1,2 @@
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:pathData="m0,0q0.4,0.05 0.6,0.3t0.3,0.3l0.1,0.4" />
\ No newline at end of file
diff --git a/tests/VectorDrawableTest/res/values/strings.xml b/tests/VectorDrawableTest/res/values/strings.xml
index b49a1aa..6ae3d7f 100644
--- a/tests/VectorDrawableTest/res/values/strings.xml
+++ b/tests/VectorDrawableTest/res/values/strings.xml
@@ -16,4 +16,11 @@
 
 <resources>
     <string name="twoLinePathData" >"M 0,0 v 100 M 0,0 h 100"</string>
+
+    <string name="triangle" > "M300,70 l 0,-70 70,70 0,0   -70,70z"</string>
+    <string name="rectangle" >"M300,70 l 0,-70 70,0  0,140 -70,0 z"</string>
+
+    <string name="rectangle2" >"M300,70 l 0,-70 70,0  0,70z M300,70  l 70,0 0,70 -70,0z"</string>
+    <string name="equal2" >    "M300,35 l 0,-35 70,0  0,35z M300,105 l 70,0 0,35 -70,0z"</string>
+
 </resources>
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java
new file mode 100644
index 0000000..36c8f2b
--- /dev/null
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/BitmapDrawableDupe.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 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.dynamic;
+
+import android.app.Activity;
+import android.content.res.Resources;
+import android.graphics.drawable.BitmapDrawable;
+import android.os.Bundle;
+import android.widget.TextView;
+import android.widget.Button;
+import android.widget.GridLayout;
+import android.widget.ScrollView;
+
+import java.text.DecimalFormat;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class BitmapDrawableDupe extends Activity {
+    private static final String LOGCAT = "VectorDrawable1";
+    protected int[] icon = {
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+            R.drawable.bitmap_drawable01,
+    };
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        ScrollView scrollView = new ScrollView(this);
+        GridLayout container = new GridLayout(this);
+        scrollView.addView(container);
+        container.setColumnCount(5);
+        container.setBackgroundColor(0xFF888888);
+
+        DecimalFormat df = new DecimalFormat("#.##");
+        long time =  android.os.SystemClock.elapsedRealtimeNanos();
+        for (int i = 0; i < icon.length; i++) {
+            Button button = new Button(this);
+            button.setWidth(200);
+            button.setBackgroundResource(icon[i]);
+            container.addView(button);
+        }
+
+        setContentView(scrollView);
+        time =  android.os.SystemClock.elapsedRealtimeNanos()-time;
+        TextView t = new TextView(this);
+        t.setText("avgS=" + df.format(time / (icon.length * 1000000.)) + " ms");
+        container.addView(t);
+    }
+}
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp
index cc0da15..816033e 100644
--- a/tools/aapt/Command.cpp
+++ b/tools/aapt/Command.cpp
@@ -407,6 +407,8 @@
     ICON_ATTR = 0x01010002,
     NAME_ATTR = 0x01010003,
     PERMISSION_ATTR = 0x01010006,
+    EXPORTED_ATTR = 0x01010010,
+    GRANT_URI_PERMISSIONS_ATTR = 0x0101001b,
     RESOURCE_ATTR = 0x01010025,
     DEBUGGABLE_ATTR = 0x0101000f,
     VALUE_ATTR = 0x01010024,
@@ -582,6 +584,10 @@
     return categories;
 }
 
+static void printComponentPresence(const char* componentName) {
+    printf("provides-component:'%s'\n", componentName);
+}
+
 /*
  * Handle the "dump" command, to extract select data from an archive.
  */
@@ -793,19 +799,27 @@
             bool withinSupportsInput = false;
             bool withinReceiver = false;
             bool withinService = false;
+            bool withinProvider = false;
             bool withinIntentFilter = false;
             bool hasMainActivity = false;
             bool hasOtherActivities = false;
             bool hasOtherReceivers = false;
             bool hasOtherServices = false;
+            bool hasIntentFilter = false;
+
             bool hasWallpaperService = false;
             bool hasImeService = false;
             bool hasAccessibilityService = false;
             bool hasPrintService = false;
             bool hasWidgetReceivers = false;
             bool hasDeviceAdminReceiver = false;
-            bool hasIntentFilter = false;
             bool hasPaymentService = false;
+            bool hasDocumentsProvider = false;
+            bool hasCameraActivity = false;
+            bool hasCameraSecureActivity = false;
+            bool hasLauncher = false;
+            bool hasNotificationListenerService = false;
+
             bool actMainActivity = false;
             bool actWidgetReceivers = false;
             bool actDeviceAdminEnabled = false;
@@ -815,6 +829,11 @@
             bool actPrintService = false;
             bool actHostApduService = false;
             bool actOffHostApduService = false;
+            bool actDocumentsProvider = false;
+            bool actNotificationListenerService = false;
+            bool actCamera = false;
+            bool actCameraSecure = false;
+            bool catLauncher = false;
             bool hasMetaHostPaymentCategory = false;
             bool hasMetaOffHostPaymentCategory = false;
 
@@ -825,6 +844,8 @@
             bool hasBindAccessibilityServicePermission = false;
             bool hasBindPrintServicePermission = false;
             bool hasBindNfcServicePermission = false;
+            bool hasRequiredSafAttributes = false;
+            bool hasBindNotificationListenerServicePermission = false;
 
             // These two implement the implicit permissions that are granted
             // to pre-1.6 applications.
@@ -962,13 +983,17 @@
                         withinActivity = false;
                         withinService = false;
                         withinReceiver = false;
+                        withinProvider = false;
                         hasIntentFilter = false;
                         isMainActivity = isLauncherActivity = isLeanbackLauncherActivity = false;
                     } else if (depth < 4) {
                         if (withinIntentFilter) {
                             if (withinActivity) {
                                 hasMainActivity |= actMainActivity;
-                                hasOtherActivities |= !actMainActivity;
+                                hasLauncher |= catLauncher;
+                                hasCameraActivity |= actCamera;
+                                hasCameraSecureActivity |= actCameraSecure;
+                                hasOtherActivities |= !actMainActivity && !actCamera && !actCameraSecure;
                             } else if (withinReceiver) {
                                 hasWidgetReceivers |= actWidgetReceivers;
                                 hasDeviceAdminReceiver |= (actDeviceAdminEnabled &&
@@ -980,9 +1005,14 @@
                                 hasAccessibilityService |= (actAccessibilityService &&
                                         hasBindAccessibilityServicePermission);
                                 hasPrintService |= (actPrintService && hasBindPrintServicePermission);
+                                hasNotificationListenerService |= actNotificationListenerService &&
+                                        hasBindNotificationListenerServicePermission;
                                 hasOtherServices |= (!actImeService && !actWallpaperService &&
                                         !actAccessibilityService && !actPrintService &&
-                                        !actHostApduService && !actOffHostApduService);
+                                        !actHostApduService && !actOffHostApduService &&
+                                        !actNotificationListenerService);
+                            } else if (withinProvider) {
+                                hasDocumentsProvider |= actDocumentsProvider && hasRequiredSafAttributes;
                             }
                         }
                         withinIntentFilter = false;
@@ -1348,6 +1378,7 @@
                     withinActivity = false;
                     withinReceiver = false;
                     withinService = false;
+                    withinProvider = false;
                     hasIntentFilter = false;
                     hasMetaHostPaymentCategory = false;
                     hasMetaOffHostPaymentCategory = false;
@@ -1356,6 +1387,8 @@
                     hasBindAccessibilityServicePermission = false;
                     hasBindPrintServicePermission = false;
                     hasBindNfcServicePermission = false;
+                    hasRequiredSafAttributes = false;
+                    hasBindNotificationListenerServicePermission = false;
                     if (withinApplication) {
                         if(tag == "activity") {
                             withinActivity = true;
@@ -1451,11 +1484,41 @@
                                     hasBindPrintServicePermission = true;
                                 } else if (permission == "android.permission.BIND_NFC_SERVICE") {
                                     hasBindNfcServicePermission = true;
+                                } else if (permission == "android.permission.BIND_NOTIFICATION_LISTENER_SERVICE") {
+                                    hasBindNotificationListenerServicePermission = true;
                                 }
                             } else {
                                 fprintf(stderr, "ERROR getting 'android:permission' attribute for"
                                         " service '%s': %s\n", serviceName.string(), error.string());
                             }
+                        } else if (tag == "provider") {
+                            withinProvider = true;
+
+                            bool exported = getResolvedIntegerAttribute(&res, tree, EXPORTED_ATTR, &error);
+                            if (error != "") {
+                                fprintf(stderr, "ERROR getting 'android:exported' attribute for provider:"
+                                        " %s\n", error.string());
+                                goto bail;
+                            }
+
+                            bool grantUriPermissions = getResolvedIntegerAttribute(&res, tree,
+                                    GRANT_URI_PERMISSIONS_ATTR, &error);
+                            if (error != "") {
+                                fprintf(stderr, "ERROR getting 'android:grantUriPermissions' attribute for provider:"
+                                        " %s\n", error.string());
+                                goto bail;
+                            }
+
+                            String8 permission = getResolvedAttribute(&res, tree, PERMISSION_ATTR, &error);
+                            if (error != "") {
+                                fprintf(stderr, "ERROR getting 'android:permission' attribute for provider:"
+                                        " %s\n", error.string());
+                                goto bail;
+                            }
+
+                            hasRequiredSafAttributes |= exported && grantUriPermissions &&
+                                permission == "android.permission.MANAGE_DOCUMENTS";
+
                         } else if (bundle->getIncludeMetaData() && tag == "meta-data") {
                             String8 metaDataName = getResolvedAttribute(&res, tree, NAME_ATTR, &error);
                             if (error != "") {
@@ -1504,6 +1567,11 @@
                         actDeviceAdminEnabled = false;
                         actHostApduService = false;
                         actOffHostApduService = false;
+                        actDocumentsProvider = false;
+                        actNotificationListenerService = false;
+                        actCamera = false;
+                        actCameraSecure = false;
+                        catLauncher = false;
                     } else if (withinService && tag == "meta-data") {
                         String8 name = getAttribute(tree, NAME_ATTR, &error);
                         if (error != "") {
@@ -1559,6 +1627,11 @@
                             if (action == "android.intent.action.MAIN") {
                                 isMainActivity = true;
                                 actMainActivity = true;
+                            } else if (action == "android.media.action.STILL_IMAGE_CAMERA" ||
+                                    action == "android.media.action.VIDEO_CAMERA") {
+                                actCamera = true;
+                            } else if (action == "android.media.action.STILL_IMAGE_CAMERA_SECURE") {
+                                actCameraSecure = true;
                             }
                         } else if (withinReceiver) {
                             if (action == "android.appwidget.action.APPWIDGET_UPDATE") {
@@ -1579,6 +1652,12 @@
                                 actHostApduService = true;
                             } else if (action == "android.nfc.cardemulation.action.OFF_HOST_APDU_SERVICE") {
                                 actOffHostApduService = true;
+                            } else if (action == "android.service.notification.NotificationListenerService") {
+                                actNotificationListenerService = true;
+                            }
+                        } else if (withinProvider) {
+                            if (action == "android.content.action.DOCUMENTS_PROVIDER") {
+                                actDocumentsProvider = true;
                             }
                         }
                         if (action == "android.intent.action.SEARCH") {
@@ -1598,6 +1677,8 @@
                                 isLauncherActivity = true;
                             } else if (category == "android.intent.category.LEANBACK_LAUNCHER") {
                                 isLeanbackLauncherActivity = true;
+                            } else if (category == "android.intent.category.HOME") {
+                                catLauncher = true;
                             }
                         }
                     }
@@ -1775,37 +1856,53 @@
                 }
             }
 
-            if (hasMainActivity) {
-                printf("main\n");
-            }
             if (hasWidgetReceivers) {
-                printf("app-widget\n");
+                printComponentPresence("app-widget");
             }
             if (hasDeviceAdminReceiver) {
-                printf("device-admin\n");
+                printComponentPresence("device-admin");
             }
             if (hasImeService) {
-                printf("ime\n");
+                printComponentPresence("ime");
             }
             if (hasWallpaperService) {
-                printf("wallpaper\n");
+                printComponentPresence("wallpaper");
             }
             if (hasAccessibilityService) {
-                printf("accessibility\n");
+                printComponentPresence("accessibility");
             }
             if (hasPrintService) {
-                printf("print\n");
+                printComponentPresence("print-service");
             }
             if (hasPaymentService) {
-                printf("payment\n");
+                printComponentPresence("payment");
+            }
+            if (isSearchable) {
+                printComponentPresence("search");
+            }
+            if (hasDocumentsProvider) {
+                printComponentPresence("document-provider");
+            }
+            if (hasLauncher) {
+                printComponentPresence("launcher");
+            }
+            if (hasNotificationListenerService) {
+                printComponentPresence("notification-listener");
+            }
+            if (hasCameraActivity) {
+                printComponentPresence("camera");
+            }
+            if (hasCameraSecureActivity) {
+                printComponentPresence("camera-secure");
+            }
+
+            if (hasMainActivity) {
+                printf("main\n");
             }
             if (hasOtherActivities) {
                 printf("other-activities\n");
             }
-            if (isSearchable) {
-                printf("search\n");
-            }
-            if (hasOtherReceivers) {
+             if (hasOtherReceivers) {
                 printf("other-receivers\n");
             }
             if (hasOtherServices) {
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index f10904c..3d93bbe 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -589,11 +589,11 @@
                 if (bundle->getVerbose()) {
                     printf("trying overlaySet Key=%s\n",overlaySet->keyAt(overlayIndex).string());
                 }
-                size_t baseIndex = UNKNOWN_ERROR;
+                ssize_t baseIndex = -1;
                 if (baseSet->get() != NULL) {
                     baseIndex = (*baseSet)->indexOfKey(overlaySet->keyAt(overlayIndex));
                 }
-                if (baseIndex < UNKNOWN_ERROR) {
+                if (baseIndex >= 0) {
                     // look for same flavor.  For a given file (strings.xml, for example)
                     // there may be a locale specific or other flavors - we want to match
                     // the same flavor.
@@ -619,10 +619,10 @@
                     for (size_t overlayGroupIndex = 0;
                             overlayGroupIndex<overlayGroupSize;
                             overlayGroupIndex++) {
-                        size_t baseFileIndex =
+                        ssize_t baseFileIndex =
                                 baseGroup->getFiles().indexOfKey(overlayFiles.
                                 keyAt(overlayGroupIndex));
-                        if (baseFileIndex < UNKNOWN_ERROR) {
+                        if (baseFileIndex >= 0) {
                             if (bundle->getVerbose()) {
                                 printf("found a match (" ZD ") for overlay file %s, for flavor %s\n",
                                         (ZD_TYPE) baseFileIndex,
@@ -1363,7 +1363,11 @@
 
             if (split->isBase()) {
                 resFile = flattenedTable;
-                finalResTable.add(flattenedTable->getData(), flattenedTable->getSize());
+                err = finalResTable.add(flattenedTable->getData(), flattenedTable->getSize());
+                if (err != NO_ERROR) {
+                    fprintf(stderr, "Generated resource table is corrupt.\n");
+                    return err;
+                }
             } else {
                 sp<AaptFile> generatedManifest = new AaptFile(String8("AndroidManifest.xml"),
                         AaptGroupEntry(), String8());
diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp
index efbba40..1a9f1b9 100644
--- a/tools/aapt/ResourceTable.cpp
+++ b/tools/aapt/ResourceTable.cpp
@@ -2292,8 +2292,14 @@
     if (resId != 0 || !createIfNotFound) {
         return resId;
     }
-    String16 value("false");
 
+    if (mAssetsPackage != package) {
+        mCurrentXmlPos.warning("creating resource for external package %s: %s/%s.",
+                String8(package).string(), String8(type).string(), String8(name).string());
+        mCurrentXmlPos.printf("This will be an error in a future version of AAPT.");
+    }
+
+    String16 value("false");
     status_t status = addEntry(mCurrentXmlPos, package, type, name, value, NULL, NULL, true);
     if (status == NO_ERROR) {
         resId = getResId(package, type, name);
@@ -3062,8 +3068,9 @@
             for (size_t i = 0; i < N; ++i) {
                 if (!validResources[i]) {
                     sp<ConfigList> c = t->getOrderedConfigs().itemAt(i);
-                    fprintf(stderr, "%s: no entries written for %s/%s\n", log_prefix,
-                            String8(typeName).string(), String8(c->getName()).string());
+                    fprintf(stderr, "%s: no entries written for %s/%s (0x%08x)\n", log_prefix,
+                            String8(typeName).string(), String8(c->getName()).string(),
+                            Res_MAKEID(p->getAssignedId() - 1, ti, i));
                     missing_entry = true;
                 }
             }
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index 08486e6..1942831 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -30,8 +30,8 @@
 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)
+built_core_dep := $(call java-lib-deps,core-libart)
+built_core_classes := $(call java-lib-files,core-libart)
 
 built_ext_dep := $(call java-lib-deps,ext)
 built_ext_classes := $(call java-lib-files,ext)
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi-v21/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/hdpi-v21/ic_sysbar_back.png
new file mode 100644
index 0000000..b28624f
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/hdpi-v21/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi-v21/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/hdpi-v21/ic_sysbar_home.png
new file mode 100644
index 0000000..3f3e288
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/hdpi-v21/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi-v21/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/hdpi-v21/ic_sysbar_recent.png
new file mode 100644
index 0000000..06dcd20
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/hdpi-v21/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi-v21/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi-v21/ic_sysbar_back.png
new file mode 100644
index 0000000..e464347
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi-v21/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi-v21/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi-v21/ic_sysbar_back.png
new file mode 100644
index 0000000..1b578a6
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi-v21/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi-v21/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi-v21/ic_sysbar_back.png
new file mode 100644
index 0000000..373e84a
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi-v21/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi-v21/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/mdpi-v21/ic_sysbar_back.png
new file mode 100644
index 0000000..f878093
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi-v21/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi-v21/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/mdpi-v21/ic_sysbar_home.png
new file mode 100644
index 0000000..8e9583b
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi-v21/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi-v21/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/mdpi-v21/ic_sysbar_recent.png
new file mode 100644
index 0000000..e2a89c3
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi-v21/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi-v21/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/xhdpi-v21/ic_sysbar_back.png
new file mode 100644
index 0000000..ec2951d
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/xhdpi-v21/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi-v21/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/xhdpi-v21/ic_sysbar_home.png
new file mode 100644
index 0000000..254f757
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/xhdpi-v21/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi-v21/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/xhdpi-v21/ic_sysbar_recent.png
new file mode 100644
index 0000000..8a8e941
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/xhdpi-v21/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
index 06673c1..9cf777d 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -16,6 +16,7 @@
 
 package android.graphics;
 
+import com.android.annotations.Nullable;
 import com.android.layoutlib.bridge.Bridge;
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.ninepatch.NinePatchChunk;
@@ -48,7 +49,7 @@
 
     @LayoutlibDelegate
     /*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage,
-            Rect padding, Options opts) {
+            @Nullable Rect padding, @Nullable Options opts) {
         Bitmap bm = null;
 
         Density density = Density.MEDIUM;
@@ -77,18 +78,20 @@
                 // put the chunk in the bitmap
                 bm.setNinePatchChunk(NinePatch_Delegate.serialize(chunk));
 
-                // read the padding
-                int[] paddingarray = chunk.getPadding();
-                padding.left = paddingarray[0];
-                padding.top = paddingarray[1];
-                padding.right = paddingarray[2];
-                padding.bottom = paddingarray[3];
+                if (padding != null) {
+                    // read the padding
+                    int[] paddingArray = chunk.getPadding();
+                    padding.left = paddingArray[0];
+                    padding.top = paddingArray[1];
+                    padding.right = paddingArray[2];
+                    padding.bottom = paddingArray[3];
+                }
             } else {
                 // load the bitmap directly.
                 bm = Bitmap_Delegate.createBitmap(is, bitmapCreateFlags, density);
             }
         } catch (IOException e) {
-            Bridge.getLog().error(null,"Failed to load image" , e, null);
+            Bridge.getLog().error(null, "Failed to load image", e, null);
         }
 
         return bm;
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index cd199dc..2ee06fc 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -627,8 +627,7 @@
                 // set the color
                 graphics.setColor(new Color(color, true /*alpha*/));
 
-                Composite composite = PorterDuffXfermode_Delegate.getComposite(
-                        PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF);
+                Composite composite = PorterDuffXfermode_Delegate.getComposite(mode, 0xFF);
                 if (composite != null) {
                     graphics.setComposite(composite);
                 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
index bf03a5e..bd934d0 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
@@ -19,6 +19,8 @@
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import java.awt.Graphics2D;
+
 /**
  * Delegate implementing the native methods of android.graphics.ColorFilter
  *
@@ -50,9 +52,17 @@
         return sManager.getDelegate(nativeShader);
     }
 
-    public abstract boolean isSupported();
     public abstract String getSupportMessage();
 
+    public boolean isSupported() {
+        return false;
+    }
+
+    public void applyFilter(Graphics2D g, int width, int height) {
+        // This should never be called directly. If supported, the sub class should override this.
+        assert false;
+    }
+
     // ---- native methods ----
 
     @LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
index 9aac2bd..6739484 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
@@ -43,11 +43,6 @@
     // ---- Public Helper methods ----
 
     @Override
-    public boolean isSupported() {
-        return false;
-    }
-
-    @Override
     public String getSupportMessage() {
         return "ColorMatrix Color Filters are not supported.";
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
index 501d55c..0dd9703 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
@@ -43,11 +43,6 @@
     // ---- Public Helper methods ----
 
     @Override
-    public boolean isSupported() {
-        return false;
-    }
-
-    @Override
     public String getSupportMessage() {
         return "Lighting Color Filters are not supported.";
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 6ee307e..5adf4ca 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -81,8 +81,8 @@
     private float mTextScaleX;
     private float mTextSkewX;
     private int mHintingMode = Paint.HINTING_ON;
-    // Variant of the font.
-    private FontVariant mFontVariant = FontVariant.NONE;
+    // Variant of the font. A paint's variant can only be compact or elegant.
+    private FontVariant mFontVariant = FontVariant.COMPACT;
 
     private Xfermode_Delegate mXfermode;
     private ColorFilter_Delegate mColorFilter;
@@ -827,8 +827,8 @@
 
         delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
 
-        // since none of those are supported, display a fidelity warning right away
-        if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
+        // Log warning if it's not supported.
+        if (delegate.mColorFilter != null && !delegate.mColorFilter.isSupported()) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
                     delegate.mColorFilter.getSupportMessage(), null, null /*data*/);
         }
@@ -873,7 +873,7 @@
         delegate.mMaskFilter = MaskFilter_Delegate.getDelegate(maskfilter);
 
         // since none of those are supported, display a fidelity warning right away
-        if (delegate.mMaskFilter != null && delegate.mMaskFilter.isSupported() == false) {
+        if (delegate.mMaskFilter != null && !delegate.mMaskFilter.isSupported()) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
                     delegate.mMaskFilter.getSupportMessage(), null, null /*data*/);
         }
@@ -906,7 +906,7 @@
         delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer);
 
         // since none of those are supported, display a fidelity warning right away
-        if (delegate.mRasterizer != null && delegate.mRasterizer.isSupported() == false) {
+        if (delegate.mRasterizer != null && !delegate.mRasterizer.isSupported()) {
             Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER,
                     delegate.mRasterizer.getSupportMessage(), null, null /*data*/);
         }
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
index 1bc3033..c44e03b 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
@@ -19,6 +19,15 @@
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import android.graphics.PorterDuff.Mode;
+
+import java.awt.AlphaComposite;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getAlphaCompositeRule;
+import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
+
 /**
  * Delegate implementing the native methods of android.graphics.PorterDuffColorFilter
  *
@@ -40,25 +49,85 @@
 
     // ---- delegate data ----
 
+    private final int mSrcColor;
+    private final Mode mMode;
+    private int mWidth;
+    private int mHeight;
+    private BufferedImage mImage;
+
+
     // ---- Public Helper methods ----
 
     @Override
     public boolean isSupported() {
+        switch (mMode) {
+        case CLEAR:
+        case SRC:
+        case SRC_IN:
+        case DST_IN:
+        case SRC_ATOP:
+            return true;
+        }
+
         return false;
     }
 
     @Override
     public String getSupportMessage() {
-        return "PorterDuff Color Filters are not supported.";
+        return "PorterDuff Color Filter is not supported for mode: " + mMode.name() + ".";
+    }
+
+    @Override
+    public void applyFilter(Graphics2D g, int width, int height) {
+        createFilterImage(width, height);
+        g.setComposite(getComposite());
+        g.drawImage(mImage, 0, 0, null);
     }
 
     // ---- native methods ----
 
     @LayoutlibDelegate
     /*package*/ static long native_CreatePorterDuffFilter(int srcColor, int porterDuffMode) {
-        PorterDuffColorFilter_Delegate newDelegate = new PorterDuffColorFilter_Delegate();
+        PorterDuffColorFilter_Delegate newDelegate =
+                new PorterDuffColorFilter_Delegate(srcColor, porterDuffMode);
         return sManager.addNewDelegate(newDelegate);
     }
 
+
     // ---- Private delegate/helper methods ----
+
+    private PorterDuffColorFilter_Delegate(int srcColor, int mode) {
+        mSrcColor = srcColor;
+        // Temporarily change multiply to SRC_IN to render menus.
+        // TODO: support Mode.MULTIPLY
+        if (mode == Mode.MULTIPLY.nativeInt) {
+            mode = Mode.SRC_IN.nativeInt;
+        }
+        mMode = getPorterDuffMode(mode);
+    }
+
+    private void createFilterImage(int width, int height) {
+        if (mWidth == width && mHeight == height && mImage != null) {
+            return;
+        }
+        mWidth = width;
+        mHeight = height;
+        mImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+        Graphics2D graphics = mImage.createGraphics();
+        try {
+            graphics.setColor(new java.awt.Color(mSrcColor, true /* hasAlpha */));
+            graphics.fillRect(0, 0, width, height);
+        } finally {
+            graphics.dispose();
+        }
+    }
+
+    private AlphaComposite getComposite() {
+        return AlphaComposite.getInstance(getAlphaCompositeRule(mMode),
+                getAlpha() / 255f);
+    }
+
+    private int getAlpha() {
+        return mSrcColor >>> 24;
+    }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
index a89fd57..f6c36b6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
@@ -21,9 +21,14 @@
 import com.android.layoutlib.bridge.impl.DelegateManager;
 import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
 
+import android.graphics.PorterDuff.Mode;
+
 import java.awt.AlphaComposite;
 import java.awt.Composite;
 
+import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getAlphaCompositeRule;
+import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
+
 /**
  * Delegate implementing the native methods of android.graphics.PorterDuffXfermode
  *
@@ -43,17 +48,17 @@
 
     // ---- delegate data ----
 
-    private final int mMode;
+    private final Mode mMode;
 
     // ---- Public Helper methods ----
 
-    public PorterDuff.Mode getMode() {
-        return getPorterDuffMode(mMode);
+    public Mode getMode() {
+        return mMode;
     }
 
     @Override
     public Composite getComposite(int alpha) {
-        return getComposite(getPorterDuffMode(mMode), alpha);
+        return getComposite(mMode, alpha);
     }
 
     @Override
@@ -67,61 +72,8 @@
         return null;
     }
 
-    public static PorterDuff.Mode getPorterDuffMode(int mode) {
-        for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
-            if (m.nativeInt == mode) {
-                return m;
-            }
-        }
-
-        Bridge.getLog().error(LayoutLog.TAG_BROKEN,
-                String.format("Unknown PorterDuff.Mode: %d", mode), null /*data*/);
-        assert false;
-        return PorterDuff.Mode.SRC_OVER;
-    }
-
-    public static Composite getComposite(PorterDuff.Mode mode, int alpha) {
-        float falpha = alpha != 0xFF ? (float)alpha / 255.f : 1.f;
-        switch (mode) {
-            case CLEAR:
-                return AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha);
-            case DARKEN:
-                break;
-            case DST:
-                return AlphaComposite.getInstance(AlphaComposite.DST, falpha);
-            case DST_ATOP:
-                return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha);
-            case DST_IN:
-                return AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha);
-            case DST_OUT:
-                return AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha);
-            case DST_OVER:
-                return AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha);
-            case LIGHTEN:
-                break;
-            case MULTIPLY:
-                break;
-            case SCREEN:
-                break;
-            case SRC:
-                return AlphaComposite.getInstance(AlphaComposite.SRC, falpha);
-            case SRC_ATOP:
-                return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha);
-            case SRC_IN:
-                return AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha);
-            case SRC_OUT:
-                return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha);
-            case SRC_OVER:
-                return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
-            case XOR:
-                return AlphaComposite.getInstance(AlphaComposite.XOR, falpha);
-        }
-
-        Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
-                String.format("Unsupported PorterDuff Mode: %s", mode.name()),
-                null, null /*data*/);
-
-        return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
+    public static Composite getComposite(int mode, int alpha) {
+        return getComposite(getPorterDuffMode(mode), alpha);
     }
 
     // ---- native methods ----
@@ -135,6 +87,20 @@
     // ---- Private delegate/helper methods ----
 
     private PorterDuffXfermode_Delegate(int mode) {
-        mMode = mode;
+        mMode = getPorterDuffMode(mode);
+    }
+
+    private static Composite getComposite(Mode mode, int alpha255) {
+        float alpha1 = alpha255 != 0xFF ? alpha255 / 255.f : 1.f;
+        int rule = getAlphaCompositeRule(mode);
+        if (rule >= 0) {
+            return AlphaComposite.getInstance(rule, alpha1);
+        }
+
+        Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
+                String.format("Unsupported PorterDuff Mode: %1$s", mode.name()),
+                null, null /*data*/);
+
+        return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
     }
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 908bb64..20ccd0c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -70,28 +70,46 @@
         return sManager.getDelegate(nativeTypeface);
     }
 
+    /**
+     * Return a list of fonts that match the style and variant. The list is ordered according to
+     * preference of fonts.
+     *
+     * @param variant The variant preferred. Can only be {@link FontVariant#COMPACT} or
+     *                {@link FontVariant#ELEGANT}
+     */
     public List<Font> getFonts(FontVariant variant) {
+        assert variant != FontVariant.NONE;
         List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
-        // If we are unable to find fonts matching the variant, we return the fonts from the
-        // other variant since we always want to draw something, rather than nothing.
-        // TODO: check this behaviour with platform.
-        List<Font> otherVariantFonts = new ArrayList<Font>();
-        for (FontFamily_Delegate ffd : mFontFamilies) {
+        for (int i = 0; i < mFontFamilies.length; i++) {
+            FontFamily_Delegate ffd = mFontFamilies[i];
             if (ffd != null) {
                 Font font = ffd.getFont(mStyle);
                 if (font != null) {
-                    if (ffd.getVariant() == variant || ffd.getVariant() == FontVariant.NONE) {
+                    FontVariant ffdVariant = ffd.getVariant();
+                    if (ffdVariant == FontVariant.NONE) {
+                        fonts.add(font);
+                        continue;
+                    }
+                    // We cannot open each font and get locales supported, etc to match the fonts.
+                    // As a workaround, we hardcode certain assumptions like Elegant and Compact
+                    // always appear in pairs.
+                    assert i < mFontFamilies.length - 1;
+                    FontFamily_Delegate ffd2 = mFontFamilies[++i];
+                    assert ffd2 != null;
+                    FontVariant ffd2Variant = ffd2.getVariant();
+                    Font font2 = ffd2.getFont(mStyle);
+                    assert ffd2Variant != FontVariant.NONE && ffd2Variant != ffdVariant
+                            && font2 != null;
+                    // Add the font with the matching variant to the list.
+                    if (variant == ffd.getVariant()) {
                         fonts.add(font);
                     } else {
-                        otherVariantFonts.add(font);
+                        fonts.add(font2);
                     }
                 }
             }
         }
-        if (fonts.size() > 0) {
-            return fonts;
-        }
-        return otherVariantFonts;
+        return fonts;
     }
 
     // ---- native methods ----
diff --git a/tools/layoutlib/bridge/src/android/view/SurfaceView.java b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
index 6aa4b3b..1e7dfbe 100644
--- a/tools/layoutlib/bridge/src/android/view/SurfaceView.java
+++ b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
@@ -45,6 +45,10 @@
         super(context, attrs, defStyle);
     }
 
+    public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+    }
+
     public SurfaceHolder getHolder() {
         return mSurfaceHolder;
     }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
index 3d50b2a..4a9f718 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -31,7 +31,11 @@
 public class MockView extends TextView {
 
     public MockView(Context context, AttributeSet attrs, int defStyle) {
-        super(context, attrs, defStyle);
+        this(context, attrs, defStyle, 0);
+    }
+
+    public MockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
 
         setText(this.getClass().getSimpleName());
         setTextColor(0xFF000000);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index 5c51c63..efd55bf 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -598,8 +598,7 @@
 
             if (item != null) {
                 // item is a reference to a style entry. Search for it.
-                item = mRenderResources.findResValue(item.getValue(),
-                        false /*forceFrameworkOnly*/);
+                item = mRenderResources.findResValue(item.getValue(), item.isFramework());
 
                 if (item instanceof StyleResourceValue) {
                     defStyleValues = (StyleResourceValue)item;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 86797e5..9bc86a0 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -26,7 +26,6 @@
 import com.android.layoutlib.bridge.impl.ResourceHelper;
 import com.android.resources.Density;
 import com.android.resources.LayoutDirection;
-import com.android.resources.ResourceType;
 
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
@@ -59,11 +58,14 @@
  */
 abstract class CustomBar extends LinearLayout {
 
+    private final int mSimulatedPlatformVersion;
+
     protected abstract TextView getStyleableTextView();
 
-    protected CustomBar(Context context, Density density, int orientation, String layoutPath,
-            String name) throws XmlPullParserException {
+    protected CustomBar(Context context, int orientation, String layoutPath,
+            String name, int simulatedPlatformVersion) throws XmlPullParserException {
         super(context);
+        mSimulatedPlatformVersion = simulatedPlatformVersion;
         setOrientation(orientation);
         if (orientation == LinearLayout.HORIZONTAL) {
             setGravity(Gravity.CENTER_VERTICAL);
@@ -100,7 +102,8 @@
             pathOut[0] = "/bars/" + density.getResourceValue() + "/" + iconName;
         }
 
-        InputStream stream = getClass().getResourceAsStream(pathOut[0]);
+        // TODO: Change this with a more generic method.
+        InputStream stream = getIconWithApi(pathOut, iconName);
         if (stream == null && tryOtherDensities) {
             for (Density d : Density.values()) {
                 if (d != density) {
@@ -121,6 +124,20 @@
         return stream;
     }
 
+    private InputStream getIconWithApi(String[] pathOut, String iconName) {
+        if (mSimulatedPlatformVersion == 0) {
+            String path = pathOut[0];
+            String dirName = path.substring(0, path.lastIndexOf('/'));
+            path = dirName + "-v21" + "/" + iconName;
+            InputStream stream = getClass().getResourceAsStream(path);
+            if (stream != null) {
+                pathOut[0] = path;
+                return stream;
+            }
+        }
+        return getClass().getResourceAsStream(pathOut[0]);
+    }
+
     protected void loadIcon(int index, String iconName, Density density) {
         loadIcon(index, iconName, density, false);
     }
@@ -158,65 +175,6 @@
         }
     }
 
-    protected void loadIcon(int index, String iconReference) {
-        ResourceValue value = getResourceValue(iconReference);
-        if (value != null) {
-            loadIcon(index, value);
-        }
-    }
-
-    protected void loadIconById(int id, String iconReference) {
-        ResourceValue value = getResourceValue(iconReference);
-        if (value != null) {
-            loadIconById(id, value);
-        }
-    }
-
-
-    protected Drawable loadIcon(int index, ResourceType type, String name) {
-        BridgeContext bridgeContext = (BridgeContext) mContext;
-        RenderResources res = bridgeContext.getRenderResources();
-
-        // find the resource
-        ResourceValue value = res.getFrameworkResource(type, name);
-
-        // resolve it if needed
-        value = res.resolveResValue(value);
-        return loadIcon(index, value);
-    }
-
-    private Drawable loadIcon(int index, ResourceValue value) {
-        View child = getChildAt(index);
-        if (child instanceof ImageView) {
-            ImageView imageView = (ImageView) child;
-
-            return loadIcon(imageView, value);
-        }
-
-        return null;
-    }
-
-    private Drawable loadIconById(int id, ResourceValue value) {
-        View child = findViewById(id);
-        if (child instanceof ImageView) {
-            ImageView imageView = (ImageView) child;
-
-            return loadIcon(imageView, value);
-        }
-
-        return null;
-    }
-
-
-    private Drawable loadIcon(ImageView imageView, ResourceValue value) {
-        Drawable drawable = ResourceHelper.getDrawable(value, (BridgeContext) mContext);
-        if (drawable != null) {
-            imageView.setImageDrawable(drawable);
-        }
-
-        return drawable;
-    }
-
     protected TextView setText(int index, String stringReference) {
         View child = getChildAt(index);
         if (child instanceof TextView) {
@@ -228,17 +186,6 @@
         return null;
     }
 
-    protected TextView setTextById(int id, String stringReference) {
-        View child = findViewById(id);
-        if (child instanceof TextView) {
-            TextView textView = (TextView) child;
-            setText(textView, stringReference);
-            return textView;
-        }
-
-        return null;
-    }
-
     private void setText(TextView textView, String stringReference) {
         ResourceValue value = getResourceValue(stringReference);
         if (value != null) {
@@ -256,7 +203,7 @@
         ResourceValue value = res.findItemInTheme(themeEntryName, true /*isFrameworkAttr*/);
         value = res.resolveResValue(value);
 
-        if (value instanceof StyleResourceValue == false) {
+        if (!(value instanceof StyleResourceValue)) {
             return;
         }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index 112c267..283ff57 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -27,8 +27,9 @@
 public class NavigationBar extends CustomBar {
 
     public NavigationBar(Context context, Density density, int orientation, boolean isRtl,
-            boolean rtlEnabled) throws XmlPullParserException {
-        super(context, density, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml");
+            boolean rtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException {
+        super(context, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml",
+                simulatedPlatformVersion);
 
         setBackgroundColor(0xFF000000);
 
@@ -44,8 +45,11 @@
             recent = 1;
         }
 
+        //noinspection SpellCheckingInspection
         loadIcon(back,   "ic_sysbar_back.png",   density, isRtl);
+        //noinspection SpellCheckingInspection
         loadIcon(2,      "ic_sysbar_home.png",   density, isRtl);
+        //noinspection SpellCheckingInspection
         loadIcon(recent, "ic_sysbar_recent.png", density, isRtl);
     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index 2421f29..c9dd777 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -18,13 +18,10 @@
 
 import com.android.layoutlib.bridge.impl.Config;
 import com.android.resources.Density;
-import com.android.resources.ResourceType;
 
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LevelListDrawable;
 import android.view.Gravity;
 import android.widget.LinearLayout;
 import android.widget.TextView;
@@ -34,7 +31,8 @@
     public StatusBar(Context context, Density density, int direction, boolean RtlEnabled,
             int simulatedPlatformVersion) throws XmlPullParserException {
         // FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar.
-        super(context, density, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml");
+        super(context, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml",
+                simulatedPlatformVersion);
 
         // FIXME: use FILL_H?
         setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
index c27859f..ff952bd 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
@@ -16,8 +16,6 @@
 
 package com.android.layoutlib.bridge.bars;
 
-import com.android.resources.Density;
-
 import org.xmlpull.v1.XmlPullParserException;
 
 import android.content.Context;
@@ -28,9 +26,10 @@
 
     private TextView mTextView;
 
-    public TitleBar(Context context, Density density, String label)
+    public TitleBar(Context context, String label, int simulatedPlatformVersion)
             throws XmlPullParserException {
-        super(context, density, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml");
+        super(context, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml",
+                simulatedPlatformVersion);
 
         // Cannot access the inside items through id because no R.id values have been
         // created for them.
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index 21d6b1a..3a0321a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -21,6 +21,7 @@
 
 import android.graphics.Bitmap_Delegate;
 import android.graphics.Canvas;
+import android.graphics.ColorFilter_Delegate;
 import android.graphics.Paint;
 import android.graphics.Paint_Delegate;
 import android.graphics.Rect;
@@ -48,8 +49,8 @@
  * This is based on top of {@link Graphics2D} but can operate independently if none are available
  * yet when setting transforms and clip information.
  * <p>
- * This allows for drawing through {@link #draw(Drawable, Paint_Delegate)} and
- * {@link #draw(Drawable, Paint_Delegate)}
+ * This allows for drawing through {@link #draw(Drawable, Paint_Delegate, boolean, boolean)} and
+ * {@link #draw(Drawable)}
  *
  * Handling of layers (created with {@link Canvas#saveLayer(RectF, Paint, int)}) is handled through
  * a list of Graphics2D for each layers. The class actually maintains a list of {@link Layer}
@@ -203,7 +204,7 @@
      * called before the snapshot can be used to draw. Transform and clip operations are permitted
      * before.
      *
-     * @param image the image to associate to the snapshot or null.
+     * @param bitmap the image to associate to the snapshot or null.
      * @return the root snapshot
      */
     public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) {
@@ -557,7 +558,6 @@
      * Executes the Drawable's draw method, with a null paint delegate.
      * <p/>
      * Note that the method can be called several times if there are more than one active layer.
-     * @param drawable
      */
     public void draw(Drawable drawable) {
         draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/);
@@ -567,20 +567,19 @@
      * Executes the Drawable's draw method.
      * <p/>
      * Note that the method can be called several times if there are more than one active layer.
-     * @param drawable
-     * @param paint
      * @param compositeOnly whether the paint is used for composite only. This is typically
      *          the case for bitmaps.
      * @param forceSrcMode if true, this overrides the composite to be SRC
      */
     public void draw(Drawable drawable, Paint_Delegate paint, boolean compositeOnly,
             boolean forceSrcMode) {
+        int forceMode = forceSrcMode ? AlphaComposite.SRC : 0;
         // the current snapshot may not have a mLocalLayer (ie it was created on save() instead
         // of saveLayer(), but that doesn't mean there's no layer.
         // mLayers however saves all the information we need (flags).
         if (mLayers.size() == 1) {
             // no layer, only base layer. easy case.
-            drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceSrcMode);
+            drawInLayer(mLayers.get(0), drawable, paint, compositeOnly, forceMode);
         } else {
             // draw in all the layers until the layer save flags tells us to stop (ie drawing
             // in that layer is limited to the layer itself.
@@ -590,7 +589,7 @@
             do {
                 Layer layer = mLayers.get(i);
 
-                drawInLayer(layer, drawable, paint, compositeOnly, forceSrcMode);
+                drawInLayer(layer, drawable, paint, compositeOnly, forceMode);
 
                 // then go to previous layer, only if there are any left, and its flags
                 // doesn't restrict drawing to the layer itself.
@@ -601,20 +600,61 @@
     }
 
     private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint,
-            boolean compositeOnly, boolean forceSrcMode) {
+            boolean compositeOnly, int forceMode) {
         Graphics2D originalGraphics = layer.getGraphics();
-        // get a Graphics2D object configured with the drawing parameters.
-        Graphics2D configuredGraphics2D =
-            paint != null ?
-                    createCustomGraphics(originalGraphics, paint, compositeOnly, forceSrcMode) :
-                        (Graphics2D) originalGraphics.create();
+        if (paint == null) {
+            drawOnGraphics((Graphics2D) originalGraphics.create(), drawable,
+                    null /*paint*/, layer);
+        } else {
+            ColorFilter_Delegate filter = paint.getColorFilter();
+            if (filter == null || !filter.isSupported()) {
+                // get a Graphics2D object configured with the drawing parameters.
+                Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
+                        compositeOnly, forceMode);
+                drawOnGraphics(configuredGraphics, drawable, paint, layer);
+                return;
+            }
 
+            int width = layer.getImage().getWidth();
+            int height = layer.getImage().getHeight();
+
+            // Create a temporary image to which the color filter will be applied.
+            BufferedImage image = new BufferedImage(width, height,
+                    BufferedImage.TYPE_INT_ARGB);
+            Graphics2D imageBaseGraphics = (Graphics2D) image.getGraphics();
+            // Configure the Graphics2D object with drawing parameters and shader.
+            Graphics2D imageGraphics = createCustomGraphics(
+                    imageBaseGraphics, paint, compositeOnly,
+                    AlphaComposite.SRC_OVER);
+            // get a Graphics2D object configured with the drawing parameters, but no shader.
+            Graphics2D configuredGraphics = createCustomGraphics(originalGraphics, paint,
+                    true /*compositeOnly*/, forceMode);
+            try {
+                // The main draw operation.
+                drawable.draw(imageGraphics, paint);
+
+                // Apply the color filter.
+                filter.applyFilter(imageGraphics, width, height);
+
+                // Draw the tinted image on the main layer.
+                configuredGraphics.drawImage(image, 0, 0, null);
+                layer.change();
+            } finally {
+                // dispose Graphics2D objects
+                imageGraphics.dispose();
+                imageBaseGraphics.dispose();
+                configuredGraphics.dispose();
+            }
+        }
+    }
+
+    private void drawOnGraphics(Graphics2D g, Drawable drawable, Paint_Delegate paint,
+            Layer layer) {
         try {
-            drawable.draw(configuredGraphics2D, paint);
+            drawable.draw(g, paint);
             layer.change();
         } finally {
-            // dispose Graphics2D object
-            configuredGraphics2D.dispose();
+            g.dispose();
         }
     }
 
@@ -685,7 +725,7 @@
         // now draw put the content of the local layer onto the layer,
         // using the paint information
         Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
-                true /*alphaOnly*/, false /*forceSrcMode*/);
+                true /*alphaOnly*/, 0 /*forceMode*/);
 
         g.drawImage(mLocalLayer.getImage(),
                 mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
@@ -701,7 +741,7 @@
      * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
      */
     private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint,
-            boolean compositeOnly, boolean forceSrcMode) {
+            boolean compositeOnly, int forceMode) {
         // make new one graphics
         Graphics2D g = (Graphics2D) original.create();
 
@@ -714,70 +754,73 @@
                     RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
         }
 
+        // set the shader first, as it'll replace the color if it can be used it.
         boolean customShader = false;
-
-        // get the shader first, as it'll replace the color if it can be used it.
-        if (compositeOnly == false) {
-            Shader_Delegate shaderDelegate = paint.getShader();
-            if (shaderDelegate != null) {
-                if (shaderDelegate.isSupported()) {
-                    java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
-                    assert shaderPaint != null;
-                    if (shaderPaint != null) {
-                        g.setPaint(shaderPaint);
-                        customShader = true;
-                    }
-                } else {
-                    Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
-                            shaderDelegate.getSupportMessage(),
-                            null /*throwable*/, null /*data*/);
-                }
-            }
-
-            // if no shader, use the paint color
-            if (customShader == false) {
-                g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
-            }
-
+        if (!compositeOnly) {
+            customShader = setShader(g, paint);
             // set the stroke
             g.setStroke(paint.getJavaStroke());
         }
+        // set the composite.
+        setComposite(g, paint, compositeOnly || customShader, forceMode);
 
-        // the alpha for the composite. Always opaque if the normal paint color is used since
-        // it contains the alpha
-        int alpha = (compositeOnly || customShader) ? paint.getAlpha() : 0xFF;
+        return g;
+    }
 
-        if (forceSrcMode) {
-            g.setComposite(AlphaComposite.getInstance(
-                    AlphaComposite.SRC, (float) alpha / 255.f));
-        } else {
-            boolean customXfermode = false;
-            Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
-            if (xfermodeDelegate != null) {
-                if (xfermodeDelegate.isSupported()) {
-                    Composite composite = xfermodeDelegate.getComposite(alpha);
-                    assert composite != null;
-                    if (composite != null) {
-                        g.setComposite(composite);
-                        customXfermode = true;
-                    }
-                } else {
-                    Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
-                            xfermodeDelegate.getSupportMessage(),
-                            null /*throwable*/, null /*data*/);
+    private boolean setShader(Graphics2D g, Paint_Delegate paint) {
+        Shader_Delegate shaderDelegate = paint.getShader();
+        if (shaderDelegate != null) {
+            if (shaderDelegate.isSupported()) {
+                java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
+                assert shaderPaint != null;
+                if (shaderPaint != null) {
+                    g.setPaint(shaderPaint);
+                    return true;
                 }
-            }
-
-            // if there was no custom xfermode, but we have alpha (due to a shader and a non
-            // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
-            // that will handle the alpha.
-            if (customXfermode == false && alpha != 0xFF) {
-                g.setComposite(AlphaComposite.getInstance(
-                        AlphaComposite.SRC_OVER, (float) alpha / 255.f));
+            } else {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
+                        shaderDelegate.getSupportMessage(),
+                        null /*throwable*/, null /*data*/);
             }
         }
 
-        return g;
+        // if no shader, use the paint color
+        g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
+
+        return false;
+    }
+
+    private void setComposite(Graphics2D g, Paint_Delegate paint, boolean usePaintAlpha,
+            int forceMode) {
+        // the alpha for the composite. Always opaque if the normal paint color is used since
+        // it contains the alpha
+        int alpha = usePaintAlpha ? paint.getAlpha() : 0xFF;
+        if (forceMode != 0) {
+            g.setComposite(AlphaComposite.getInstance(forceMode, (float) alpha / 255.f));
+            return;
+        }
+        Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
+        if (xfermodeDelegate != null) {
+            if (xfermodeDelegate.isSupported()) {
+                Composite composite = xfermodeDelegate.getComposite(alpha);
+                assert composite != null;
+                if (composite != null) {
+                    g.setComposite(composite);
+                    return;
+                }
+            } else {
+                Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
+                        xfermodeDelegate.getSupportMessage(),
+                        null /*throwable*/, null /*data*/);
+            }
+        }
+        // if there was no custom xfermode, but we have alpha (due to a shader and a non
+        // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
+        // that will handle the alpha.
+        if (alpha != 0xFF) {
+            g.setComposite(AlphaComposite.getInstance(
+                    AlphaComposite.SRC_OVER, (float) alpha / 255.f));
+        }
     }
 
     private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
new file mode 100644
index 0000000..bc53e93
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014 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.layoutlib.bridge.impl;
+
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter_Delegate;
+import android.graphics.PorterDuffXfermode_Delegate;
+
+import java.awt.AlphaComposite;
+
+/**
+ * Provides various utility methods for {@link PorterDuffColorFilter_Delegate} and {@link
+ * PorterDuffXfermode_Delegate}.
+ */
+public final class PorterDuffUtility {
+
+    // Make the class non-instantiable.
+    private PorterDuffUtility() {
+    }
+
+    /**
+     * Convert the porterDuffMode from the framework to its corresponding enum. This defaults to
+     * {@link Mode#SRC_OVER} for invalid modes.
+     */
+    public static Mode getPorterDuffMode(int porterDuffMode) {
+        Mode[] values = Mode.values();
+        if (porterDuffMode >= 0 && porterDuffMode < values.length) {
+            return values[porterDuffMode];
+        }
+        Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+                String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null /*data*/);
+        assert false;
+        return Mode.SRC_OVER;
+    }
+
+    /**
+     * A utility method to convert the porterDuffMode to an int to be used as a rule for {@link
+     * AlphaComposite}. If {@code AlphaComposite} doesn't support the mode, -1 is returned.
+     */
+    public static int getAlphaCompositeRule(Mode porterDuffMode) {
+        switch (porterDuffMode) {
+            case CLEAR:
+                return AlphaComposite.CLEAR;
+            case DARKEN:
+                break;
+            case DST:
+                return AlphaComposite.DST;
+            case DST_ATOP:
+                return AlphaComposite.DST_ATOP;
+            case DST_IN:
+                return AlphaComposite.DST_IN;
+            case DST_OUT:
+                return AlphaComposite.DST_OUT;
+            case DST_OVER:
+                return AlphaComposite.DST_OVER;
+            case LIGHTEN:
+                break;
+            case MULTIPLY:
+                break;
+            case SCREEN:
+                break;
+            case SRC:
+                return AlphaComposite.SRC;
+            case SRC_ATOP:
+                return AlphaComposite.SRC_ATOP;
+            case SRC_IN:
+                return AlphaComposite.SRC_IN;
+            case SRC_OUT:
+                return AlphaComposite.SRC_OUT;
+            case SRC_OVER:
+                return AlphaComposite.SRC_OVER;
+            case XOR:
+                return AlphaComposite.XOR;
+        }
+        // This is an unsupported mode.
+        return -1;
+
+    }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index c715003..07f72c4 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -247,6 +247,7 @@
                 backgroundView = mViewRoot = mContentRoot = new FrameLayout(context);
                 mViewRoot.setLayoutDirection(layoutDirection);
             } else {
+                int simulatedPlatformVersion = params.getSimulatedPlatformVersion();
                 if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) {
                     /*
                      * This is a special case where the navigation bar is on the right.
@@ -271,10 +272,11 @@
                     mViewRoot = topLayout;
                     topLayout.setOrientation(LinearLayout.HORIZONTAL);
 
-                    if (Config.showOnScreenNavBar(params.getSimulatedPlatformVersion())) {
+                    if (Config.showOnScreenNavBar(simulatedPlatformVersion)) {
                         try {
                             NavigationBar navigationBar = createNavigationBar(context,
-                                    hardwareConfig.getDensity(), isRtl, params.isRtlSupported());
+                                    hardwareConfig.getDensity(), isRtl, params.isRtlSupported(),
+                                    simulatedPlatformVersion);
                             topLayout.addView(navigationBar);
                         } catch (XmlPullParserException ignored) {
                         }
@@ -331,7 +333,7 @@
                     try {
                         StatusBar statusBar = createStatusBar(context, hardwareConfig.getDensity(),
                                 layoutDirection, params.isRtlSupported(),
-                                params.getSimulatedPlatformVersion());
+                                simulatedPlatformVersion);
                         topLayout.addView(statusBar);
                     } catch (XmlPullParserException ignored) {
 
@@ -357,7 +359,8 @@
                 } else if (mTitleBarSize > 0) {
                     try {
                         TitleBar titleBar = createTitleBar(context,
-                                hardwareConfig.getDensity(), params.getAppLabel());
+                                params.getAppLabel(),
+                                simulatedPlatformVersion);
                         backgroundLayout.addView(titleBar);
                     } catch (XmlPullParserException ignored) {
 
@@ -374,13 +377,14 @@
                     backgroundLayout.addView(mContentRoot);
                 }
 
-                if (Config.showOnScreenNavBar(params.getSimulatedPlatformVersion()) &&
+                if (Config.showOnScreenNavBar(simulatedPlatformVersion) &&
                         mNavigationBarOrientation == LinearLayout.HORIZONTAL &&
                         mNavigationBarSize > 0) {
                     // system bar
                     try {
                         NavigationBar navigationBar = createNavigationBar(context,
-                                hardwareConfig.getDensity(), isRtl, params.isRtlSupported());
+                                hardwareConfig.getDensity(), isRtl, params.isRtlSupported(),
+                                simulatedPlatformVersion);
                         topLayout.addView(navigationBar);
                     } catch (XmlPullParserException ignored) {
 
@@ -1592,10 +1596,11 @@
      *        is RTL aware.
      */
     private NavigationBar createNavigationBar(BridgeContext context, Density density,
-            boolean isRtl, boolean isRtlSupported) throws XmlPullParserException {
+            boolean isRtl, boolean isRtlSupported, int simulatedPlatformVersion)
+            throws XmlPullParserException {
         NavigationBar navigationBar = new NavigationBar(context,
                 density, mNavigationBarOrientation, isRtl,
-                isRtlSupported);
+                isRtlSupported, simulatedPlatformVersion);
         if (mNavigationBarOrientation == LinearLayout.VERTICAL) {
             navigationBar.setLayoutParams(new LinearLayout.LayoutParams(mNavigationBarSize,
                     LayoutParams.MATCH_PARENT));
@@ -1606,9 +1611,10 @@
         return navigationBar;
     }
 
-    private TitleBar createTitleBar(BridgeContext context, Density density, String title)
+    private TitleBar createTitleBar(BridgeContext context, String title,
+            int simulatedPlatformVersion)
             throws XmlPullParserException {
-        TitleBar titleBar = new TitleBar(context, density, title);
+        TitleBar titleBar = new TitleBar(context, title, simulatedPlatformVersion);
         titleBar.setLayoutParams(
                 new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, mTitleBarSize));
         return titleBar;
diff --git a/wifi/java/android/net/wifi/IWifiManager.aidl b/wifi/java/android/net/wifi/IWifiManager.aidl
index 00e1cd8..e83eed7 100644
--- a/wifi/java/android/net/wifi/IWifiManager.aidl
+++ b/wifi/java/android/net/wifi/IWifiManager.aidl
@@ -132,5 +132,13 @@
     void enableVerboseLogging(int verbose);
 
     int getVerboseLoggingLevel();
+
+    int getAggressiveHandover();
+
+    void enableAggressiveHandover(int enabled);
+
+    int getAllowScansWithTraffic();
+
+    void setAllowScansWithTraffic(int enabled);
 }
 
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index b64ad60..59b48e4 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -373,19 +373,19 @@
     public static int GOOD_RSSI_24 = -65;
 
     /** @hide **/
-    public static int LOW_RSSI_24 = -75;
+    public static int LOW_RSSI_24 = -77;
 
     /** @hide **/
-    public static int BAD_RSSI_24 = -85;
+    public static int BAD_RSSI_24 = -87;
 
     /** @hide **/
-    public static int GOOD_RSSI_5 = -55;
+    public static int GOOD_RSSI_5 = -60;
 
     /** @hide **/
-    public static int LOW_RSSI_5 = -65;
+    public static int LOW_RSSI_5 = -72;
 
     /** @hide **/
-    public static int BAD_RSSI_5 = -75;
+    public static int BAD_RSSI_5 = -82;
 
     /** @hide **/
     public static int UNWANTED_BLACKLIST_SOFT_BUMP = 4;
@@ -394,7 +394,7 @@
     public static int UNWANTED_BLACKLIST_HARD_BUMP = 8;
 
     /** @hide **/
-    public static int UNBLACKLIST_THRESHOLD_24_SOFT = -75;
+    public static int UNBLACKLIST_THRESHOLD_24_SOFT = -77;
 
     /** @hide **/
     public static int UNBLACKLIST_THRESHOLD_24_HARD = -68;
@@ -415,6 +415,19 @@
      * 5GHz band is prefered over 2.4 if the 5GHz RSSI is higher than this threshold **/
     public static int A_BAND_PREFERENCE_RSSI_THRESHOLD = -65;
 
+    /** @hide
+     * 5GHz band is penalized if the 5GHz RSSI is lower than this threshold **/
+    public static int G_BAND_PREFERENCE_RSSI_THRESHOLD = -75;
+
+    /** @hide
+     * Boost given to RSSI on a home network for the purpose of calculating the score
+     * This adds stickiness to home networks, as defined by:
+     * - less than 4 known BSSIDs
+     * - PSK only
+     * - TODO: add a test to verify that all BSSIDs are behind same gateway
+     ***/
+    public static int HOME_NETWORK_RSSI_BOOST = 5;
+
     /**
      * @hide
      * A summary of the RSSI and Band status for that configuration
diff --git a/wifi/java/android/net/wifi/WifiInfo.java b/wifi/java/android/net/wifi/WifiInfo.java
index 54a7df2..e46f916 100644
--- a/wifi/java/android/net/wifi/WifiInfo.java
+++ b/wifi/java/android/net/wifi/WifiInfo.java
@@ -134,6 +134,11 @@
     /**
      * @hide
      */
+    public int linkStuckCount;
+
+    /**
+     * @hide
+     */
     public int lowRssiCount;
 
     /**
@@ -237,6 +242,7 @@
         txRetriesRate = 0;
         lowRssiCount = 0;
         badRssiCount = 0;
+        linkStuckCount = 0;
         score = 0;
     }
 
@@ -267,6 +273,7 @@
             score = source.score;
             badRssiCount = source.badRssiCount;
             lowRssiCount = source.lowRssiCount;
+            linkStuckCount = source.linkStuckCount;
         }
     }
 
diff --git a/wifi/java/android/net/wifi/WifiManager.java b/wifi/java/android/net/wifi/WifiManager.java
index 141a69e..a30fb79 100644
--- a/wifi/java/android/net/wifi/WifiManager.java
+++ b/wifi/java/android/net/wifi/WifiManager.java
@@ -2226,7 +2226,6 @@
         }
     }
 
-
     /**
      * Set wifi verbose log. Called from developer settings.
      * @hide
@@ -2251,4 +2250,53 @@
             return 0;
         }
     }
+
+    /**
+     * Set wifi Aggressive Handover. Called from developer settings.
+     * @hide
+     */
+    public void enableAggressiveHandover(int enabled) {
+        try {
+            mService.enableAggressiveHandover(enabled);
+        } catch (RemoteException e) {
+
+        }
+    }
+
+    /**
+     * Get the WiFi Handover aggressiveness.This is used by settings
+     * to decide what to show within the picker.
+     * @hide
+     */
+    public int getAggressiveHandover() {
+        try {
+            return mService.getAggressiveHandover();
+        } catch (RemoteException e) {
+            return 0;
+        }
+    }
+
+    /**
+     * Set setting for allowing Scans when traffic is ongoing.
+     * @hide
+     */
+    public void setAllowScansWithTraffic(int enabled) {
+        try {
+            mService.setAllowScansWithTraffic(enabled);
+        } catch (RemoteException e) {
+
+        }
+    }
+
+    /**
+     * Get setting for allowing Scans when traffic is ongoing.
+     * @hide
+     */
+    public int getAllowScansWithTraffic() {
+        try {
+            return mService.getAllowScansWithTraffic();
+        } catch (RemoteException e) {
+            return 0;
+        }
+    }
 }